From c5df97a4ccc1c8c9cd54ae3c8cb97fba0f80bc7b Mon Sep 17 00:00:00 2001 From: amia <amia@dtu.dk> Date: Fri, 12 Apr 2024 11:54:16 +0000 Subject: [PATCH] Dev_to_main --- .gitignore | 163 + .gitlab-ci.yml | 7 +- .gitmodules | 4 + .../Edwin_interarray_cbc_IEA_colab.ipynb | 4351 +++++++++++++++++ .../Edwin_interarray_cplex_IEA.ipynb | 1009 ++++ .../IEA37_Borssele/IEA37_10MW_Turbine.yaml | 13 + .../IEA37_Borssele_Irregular.yaml | 53 + .../IEA37_Borssele_Irregular_System.yaml | 9 + .../IEA37_Borssele_Regular.yaml | 43 + .../IEA37_Borssele_Regular_System.yaml | 9 + .../IEA37_Borssele/IEA37_Borssele_Site.yaml | 10 + .../IEA37_Borssele_Wind_Resource.yaml | 115 + .../IEA37_Borssele_Cable_Optimization.ipynb | 2603 ++++++++++ ed_win/drivers/__init__.py | 0 ed_win/drivers/drivers_api.py | 121 + ed_win/drivers/ga/CostFunction.py | 232 + ed_win/drivers/ga/Crossover.py | 53 + ed_win/drivers/ga/FW_2_Solver_GA.py | 500 ++ ed_win/drivers/ga/GA.py | 532 ++ ed_win/drivers/ga/Mutate.py | 32 + ed_win/drivers/ga/RouletteWheelSelection.py | 52 + ed_win/drivers/ga/TwoLinesIntersecting.py | 136 + ed_win/drivers/ga/__init__.py | 0 ed_win/drivers/ga/data.py | 260 + ed_win/drivers/ga/dict_to_class.py | 21 + ed_win/drivers/interarray | 1 + ed_win/{ => drivers/tsh}/c_mst.py | 22 +- ed_win/{ => drivers/tsh}/c_mst_cables.py | 227 +- ed_win/{ => drivers/tsh}/collection_system.py | 15 +- .../{ => drivers/tsh}/intersection_checker.py | 0 .../tsh}/two_lines_intersecting.py | 0 ed_win/edwin_lib.py | 243 + ed_win/examples/__init__.py | 2 + .../examples/example_optimize_vs_evaluate.py | 178 + ed_win/plotting.py | 261 + .../plotting_scripts/ClassicEsauWilliams.py | 387 ++ .../plotting_scripts/CrossingPreventingEW.py | 594 +++ ed_win/plotting_scripts/NonBranchingEW.py | 629 +++ .../plotting_scripts/ObstacleBypassingEW.py | 1280 +++++ ed_win/plotting_scripts/__init__.py | 9 + ed_win/plotting_scripts/augmentation.py | 351 ++ ed_win/plotting_scripts/clusterlib.py | 359 ++ ed_win/plotting_scripts/crossings.py | 309 ++ ed_win/plotting_scripts/dbmodel.py | 92 + ed_win/plotting_scripts/farmrepo.py | 66 + ed_win/plotting_scripts/farmrepo_landscape.py | 72 + ed_win/plotting_scripts/fileio.py | 123 + ed_win/plotting_scripts/geometric.py | 1152 +++++ ed_win/plotting_scripts/heuristics.py | 7 + ed_win/plotting_scripts/importer.py | 109 + ed_win/plotting_scripts/interarraylib.py | 284 ++ ed_win/plotting_scripts/interface.py | 213 + ed_win/plotting_scripts/new_dbmodel.py | 93 + ed_win/plotting_scripts/new_storage.py | 285 ++ ed_win/plotting_scripts/pathfinding.py | 1058 ++++ ed_win/plotting_scripts/plotting.py | 575 +++ ed_win/plotting_scripts/priorityqueue.py | 46 + ed_win/plotting_scripts/storage.py | 254 + ed_win/plotting_scripts/svg.py | 228 + ed_win/plotting_scripts/synthetic.py | 180 + ed_win/plotting_scripts/utils.py | 80 + ed_win/plotting_scripts/weighting.py | 19 + ed_win/tests/test_wind_farm_network.py | 128 +- ed_win/wind_farm_network.py | 519 +- setup.py | 22 +- 65 files changed, 20398 insertions(+), 402 deletions(-) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 docs/notebooks/Edwin_interarray_cbc_IEA_colab.ipynb create mode 100644 docs/notebooks/Edwin_interarray_cplex_IEA.ipynb create mode 100644 docs/notebooks/IEA37_Borssele/IEA37_10MW_Turbine.yaml create mode 100644 docs/notebooks/IEA37_Borssele/IEA37_Borssele_Irregular.yaml create mode 100644 docs/notebooks/IEA37_Borssele/IEA37_Borssele_Irregular_System.yaml create mode 100644 docs/notebooks/IEA37_Borssele/IEA37_Borssele_Regular.yaml create mode 100644 docs/notebooks/IEA37_Borssele/IEA37_Borssele_Regular_System.yaml create mode 100644 docs/notebooks/IEA37_Borssele/IEA37_Borssele_Site.yaml create mode 100644 docs/notebooks/IEA37_Borssele/IEA37_Borssele_Wind_Resource.yaml create mode 100644 docs/notebooks/IEA37_Borssele_Cable_Optimization.ipynb create mode 100644 ed_win/drivers/__init__.py create mode 100644 ed_win/drivers/drivers_api.py create mode 100644 ed_win/drivers/ga/CostFunction.py create mode 100644 ed_win/drivers/ga/Crossover.py create mode 100644 ed_win/drivers/ga/FW_2_Solver_GA.py create mode 100644 ed_win/drivers/ga/GA.py create mode 100644 ed_win/drivers/ga/Mutate.py create mode 100644 ed_win/drivers/ga/RouletteWheelSelection.py create mode 100644 ed_win/drivers/ga/TwoLinesIntersecting.py create mode 100644 ed_win/drivers/ga/__init__.py create mode 100644 ed_win/drivers/ga/data.py create mode 100644 ed_win/drivers/ga/dict_to_class.py create mode 160000 ed_win/drivers/interarray rename ed_win/{ => drivers/tsh}/c_mst.py (96%) rename ed_win/{ => drivers/tsh}/c_mst_cables.py (90%) rename ed_win/{ => drivers/tsh}/collection_system.py (85%) rename ed_win/{ => drivers/tsh}/intersection_checker.py (100%) rename ed_win/{ => drivers/tsh}/two_lines_intersecting.py (100%) create mode 100644 ed_win/edwin_lib.py create mode 100644 ed_win/examples/__init__.py create mode 100644 ed_win/examples/example_optimize_vs_evaluate.py create mode 100644 ed_win/plotting.py create mode 100644 ed_win/plotting_scripts/ClassicEsauWilliams.py create mode 100644 ed_win/plotting_scripts/CrossingPreventingEW.py create mode 100644 ed_win/plotting_scripts/NonBranchingEW.py create mode 100644 ed_win/plotting_scripts/ObstacleBypassingEW.py create mode 100644 ed_win/plotting_scripts/__init__.py create mode 100644 ed_win/plotting_scripts/augmentation.py create mode 100644 ed_win/plotting_scripts/clusterlib.py create mode 100644 ed_win/plotting_scripts/crossings.py create mode 100644 ed_win/plotting_scripts/dbmodel.py create mode 100644 ed_win/plotting_scripts/farmrepo.py create mode 100644 ed_win/plotting_scripts/farmrepo_landscape.py create mode 100644 ed_win/plotting_scripts/fileio.py create mode 100644 ed_win/plotting_scripts/geometric.py create mode 100644 ed_win/plotting_scripts/heuristics.py create mode 100644 ed_win/plotting_scripts/importer.py create mode 100644 ed_win/plotting_scripts/interarraylib.py create mode 100644 ed_win/plotting_scripts/interface.py create mode 100644 ed_win/plotting_scripts/new_dbmodel.py create mode 100644 ed_win/plotting_scripts/new_storage.py create mode 100644 ed_win/plotting_scripts/pathfinding.py create mode 100644 ed_win/plotting_scripts/plotting.py create mode 100644 ed_win/plotting_scripts/priorityqueue.py create mode 100644 ed_win/plotting_scripts/storage.py create mode 100644 ed_win/plotting_scripts/svg.py create mode 100644 ed_win/plotting_scripts/synthetic.py create mode 100644 ed_win/plotting_scripts/utils.py create mode 100644 ed_win/plotting_scripts/weighting.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3fce0bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +# files not to be sent to repo + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 483bce6..71250cf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,7 +6,8 @@ check_code_style: test script: - pip install -e . - - pycodestyle --ignore=E501,W504,E741 ed_win + - pycodestyle --ignore=E501,W504,E741 --exclude=interarray/* ed_win + tags: - python @@ -15,7 +16,7 @@ test_EDWIN: stage: test script: - - pip install -e .[test] --timeout 60 + - pip install -e .[test,interarray] --timeout 60 - pytest tags: - ci-ubuntu @@ -69,3 +70,5 @@ pypi: - twine upload dist/* -u $TWINE_USERNAME -p $TWINE_PASSWORD tags: - python + + diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..06b8097 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "ed_win/drivers/interarray"] + path = ed_win/drivers/interarray + url = https://github.com/mdealencar/interarray.git + branch = as_submodule diff --git a/docs/notebooks/Edwin_interarray_cbc_IEA_colab.ipynb b/docs/notebooks/Edwin_interarray_cbc_IEA_colab.ipynb new file mode 100644 index 0000000..dcade31 --- /dev/null +++ b/docs/notebooks/Edwin_interarray_cbc_IEA_colab.ipynb @@ -0,0 +1,4351 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "I-EuP-xEgNuM", + "outputId": "f3e71a6c-8ec1-443a-a293-258c9ef895a9" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Package Version\n", + "-------------------------------- ---------------------\n", + "absl-py 1.4.0\n", + "aiohttp 3.9.3\n", + "aiosignal 1.3.1\n", + "alabaster 0.7.16\n", + "albumentations 1.3.1\n", + "altair 4.2.2\n", + "anyio 3.7.1\n", + "appdirs 1.4.4\n", + "argon2-cffi 23.1.0\n", + "argon2-cffi-bindings 21.2.0\n", + "array-record 0.5.0\n", + "arviz 0.15.1\n", + "astropy 5.3.4\n", + "astunparse 1.6.3\n", + "async-timeout 4.0.3\n", + "atpublic 4.0\n", + "attrs 23.2.0\n", + "audioread 3.0.1\n", + "autograd 1.6.2\n", + "Babel 2.14.0\n", + "backcall 0.2.0\n", + "beautifulsoup4 4.12.3\n", + "bidict 0.22.1\n", + "bigframes 0.20.0\n", + "bleach 6.1.0\n", + "blinker 1.4\n", + "blis 0.7.11\n", + "blosc2 2.0.0\n", + "bokeh 3.3.4\n", + "bqplot 0.12.42\n", + "branca 0.7.1\n", + "build 1.0.3\n", + "CacheControl 0.13.1\n", + "cachetools 5.3.2\n", + "catalogue 2.0.10\n", + "certifi 2023.11.17\n", + "cffi 1.16.0\n", + "chardet 5.2.0\n", + "charset-normalizer 3.3.2\n", + "chex 0.1.7\n", + "click 8.1.7\n", + "click-plugins 1.1.1\n", + "cligj 0.7.2\n", + "cloudpathlib 0.16.0\n", + "cloudpickle 2.2.1\n", + "cmake 3.27.9\n", + "cmdstanpy 1.2.0\n", + "colorcet 3.0.1\n", + "colorlover 0.3.0\n", + "colour 0.1.5\n", + "community 1.0.0b1\n", + "confection 0.1.4\n", + "cons 0.4.6\n", + "contextlib2 21.6.0\n", + "contourpy 1.2.0\n", + "cryptography 42.0.2\n", + "cufflinks 0.17.3\n", + "cupy-cuda12x 12.2.0\n", + "cvxopt 1.3.2\n", + "cvxpy 1.3.3\n", + "cycler 0.12.1\n", + "cymem 2.0.8\n", + "Cython 3.0.8\n", + "dask 2023.8.1\n", + "datascience 0.17.6\n", + "db-dtypes 1.2.0\n", + "dbus-python 1.2.18\n", + "debugpy 1.6.6\n", + "decorator 4.4.2\n", + "defusedxml 0.7.1\n", + "diskcache 5.6.3\n", + "distributed 2023.8.1\n", + "distro 1.7.0\n", + "dlib 19.24.2\n", + "dm-tree 0.1.8\n", + "docutils 0.18.1\n", + "dopamine-rl 4.0.6\n", + "duckdb 0.9.2\n", + "earthengine-api 0.1.388\n", + "easydict 1.11\n", + "ecos 2.0.12\n", + "editdistance 0.6.2\n", + "eerepr 0.0.4\n", + "en-core-web-sm 3.7.1\n", + "entrypoints 0.4\n", + "et-xmlfile 1.1.0\n", + "etils 1.6.0\n", + "etuples 0.3.9\n", + "exceptiongroup 1.2.0\n", + "fastai 2.7.14\n", + "fastcore 1.5.29\n", + "fastdownload 0.0.7\n", + "fastjsonschema 2.19.1\n", + "fastprogress 1.0.3\n", + "fastrlock 0.8.2\n", + "filelock 3.13.1\n", + "fiona 1.9.5\n", + "firebase-admin 5.3.0\n", + "Flask 2.2.5\n", + "flatbuffers 23.5.26\n", + "flax 0.8.0\n", + "folium 0.14.0\n", + "fonttools 4.47.2\n", + "frozendict 2.4.0\n", + "frozenlist 1.4.1\n", + "fsspec 2023.6.0\n", + "future 0.18.3\n", + "gast 0.5.4\n", + "gcsfs 2023.6.0\n", + "GDAL 3.6.4\n", + "gdown 4.7.3\n", + "geemap 0.30.4\n", + "gensim 4.3.2\n", + "geocoder 1.38.1\n", + "geographiclib 2.0\n", + "geopandas 0.13.2\n", + "geopy 2.3.0\n", + "gin-config 0.5.0\n", + "glob2 0.7\n", + "google 2.0.3\n", + "google-ai-generativelanguage 0.4.0\n", + "google-api-core 2.11.1\n", + "google-api-python-client 2.84.0\n", + "google-auth 2.17.3\n", + "google-auth-httplib2 0.1.1\n", + "google-auth-oauthlib 1.2.0\n", + "google-cloud-aiplatform 1.39.0\n", + "google-cloud-bigquery 3.12.0\n", + "google-cloud-bigquery-connection 1.12.1\n", + "google-cloud-bigquery-storage 2.24.0\n", + "google-cloud-core 2.3.3\n", + "google-cloud-datastore 2.15.2\n", + "google-cloud-firestore 2.11.1\n", + "google-cloud-functions 1.13.3\n", + "google-cloud-iam 2.13.0\n", + "google-cloud-language 2.9.1\n", + "google-cloud-resource-manager 1.11.0\n", + "google-cloud-storage 2.8.0\n", + "google-cloud-translate 3.11.3\n", + "google-colab 1.0.0\n", + "google-crc32c 1.5.0\n", + "google-generativeai 0.3.2\n", + "google-pasta 0.2.0\n", + "google-resumable-media 2.7.0\n", + "googleapis-common-protos 1.62.0\n", + "googledrivedownloader 0.4\n", + "graphviz 0.20.1\n", + "greenlet 3.0.3\n", + "grpc-google-iam-v1 0.13.0\n", + "grpcio 1.60.0\n", + "grpcio-status 1.48.2\n", + "gspread 3.4.2\n", + "gspread-dataframe 3.3.1\n", + "gym 0.25.2\n", + "gym-notices 0.0.8\n", + "h5netcdf 1.3.0\n", + "h5py 3.9.0\n", + "holidays 0.41\n", + "holoviews 1.17.1\n", + "html5lib 1.1\n", + "httpimport 1.3.1\n", + "httplib2 0.22.0\n", + "huggingface-hub 0.20.3\n", + "humanize 4.7.0\n", + "hyperopt 0.2.7\n", + "ibis-framework 7.1.0\n", + "idna 3.6\n", + "imageio 2.31.6\n", + "imageio-ffmpeg 0.4.9\n", + "imagesize 1.4.1\n", + "imbalanced-learn 0.10.1\n", + "imgaug 0.4.0\n", + "importlib-metadata 7.0.1\n", + "importlib-resources 6.1.1\n", + "imutils 0.5.4\n", + "inflect 7.0.0\n", + "iniconfig 2.0.0\n", + "install 1.3.5\n", + "intel-openmp 2023.2.3\n", + "ipyevents 2.0.2\n", + "ipyfilechooser 0.6.0\n", + "ipykernel 5.5.6\n", + "ipyleaflet 0.18.2\n", + "ipython 7.34.0\n", + "ipython-genutils 0.2.0\n", + "ipython-sql 0.5.0\n", + "ipytree 0.2.2\n", + "ipywidgets 7.7.1\n", + "itsdangerous 2.1.2\n", + "jax 0.4.23\n", + "jaxlib 0.4.23+cuda12.cudnn89\n", + "jeepney 0.7.1\n", + "jieba 0.42.1\n", + "Jinja2 3.1.3\n", + "joblib 1.3.2\n", + "jsonpickle 3.0.2\n", + "jsonschema 4.19.2\n", + "jsonschema-specifications 2023.12.1\n", + "jupyter-client 6.1.12\n", + "jupyter-console 6.1.0\n", + "jupyter_core 5.7.1\n", + "jupyter-server 1.24.0\n", + "jupyterlab_pygments 0.3.0\n", + "jupyterlab-widgets 3.0.9\n", + "kaggle 1.5.16\n", + "kagglehub 0.1.8\n", + "keras 2.15.0\n", + "keyring 23.5.0\n", + "kiwisolver 1.4.5\n", + "langcodes 3.3.0\n", + "launchpadlib 1.10.16\n", + "lazr.restfulclient 0.14.4\n", + "lazr.uri 1.0.6\n", + "lazy_loader 0.3\n", + "libclang 16.0.6\n", + "librosa 0.10.1\n", + "lida 0.0.10\n", + "lightgbm 4.1.0\n", + "linkify-it-py 2.0.2\n", + "llmx 0.0.15a0\n", + "llvmlite 0.41.1\n", + "locket 1.0.0\n", + "logical-unification 0.4.6\n", + "lxml 4.9.4\n", + "malloy 2023.1067\n", + "Markdown 3.5.2\n", + "markdown-it-py 3.0.0\n", + "MarkupSafe 2.1.4\n", + "matplotlib 3.7.1\n", + "matplotlib-inline 0.1.6\n", + "matplotlib-venn 0.11.10\n", + "mdit-py-plugins 0.4.0\n", + "mdurl 0.1.2\n", + "miniKanren 1.0.3\n", + "missingno 0.5.2\n", + "mistune 0.8.4\n", + "mizani 0.9.3\n", + "mkl 2023.2.0\n", + "ml-dtypes 0.2.0\n", + "mlxtend 0.22.0\n", + "more-itertools 10.1.0\n", + "moviepy 1.0.3\n", + "mpmath 1.3.0\n", + "msgpack 1.0.7\n", + "multidict 6.0.4\n", + "multipledispatch 1.0.0\n", + "multitasking 0.0.11\n", + "murmurhash 1.0.10\n", + "music21 9.1.0\n", + "natsort 8.4.0\n", + "nbclassic 1.0.0\n", + "nbclient 0.9.0\n", + "nbconvert 6.5.4\n", + "nbformat 5.9.2\n", + "nest-asyncio 1.6.0\n", + "networkx 3.2.1\n", + "nibabel 4.0.2\n", + "nltk 3.8.1\n", + "notebook 6.5.5\n", + "notebook_shim 0.2.3\n", + "numba 0.58.1\n", + "numexpr 2.9.0\n", + "numpy 1.23.5\n", + "oauth2client 4.1.3\n", + "oauthlib 3.2.2\n", + "opencv-contrib-python 4.8.0.76\n", + "opencv-python 4.8.0.76\n", + "opencv-python-headless 4.9.0.80\n", + "openpyxl 3.1.2\n", + "opt-einsum 3.3.0\n", + "optax 0.1.8\n", + "orbax-checkpoint 0.4.4\n", + "osqp 0.6.2.post8\n", + "packaging 23.2\n", + "pandas 1.5.3\n", + "pandas-datareader 0.10.0\n", + "pandas-gbq 0.19.2\n", + "pandas-stubs 1.5.3.230304\n", + "pandocfilters 1.5.1\n", + "panel 1.3.8\n", + "param 2.0.2\n", + "parso 0.8.3\n", + "parsy 2.1\n", + "partd 1.4.1\n", + "pathlib 1.0.1\n", + "patsy 0.5.6\n", + "peewee 3.17.0\n", + "pexpect 4.9.0\n", + "pickleshare 0.7.5\n", + "Pillow 9.4.0\n", + "pins 0.8.4\n", + "pip 23.1.2\n", + "pip-tools 6.13.0\n", + "platformdirs 4.2.0\n", + "plotly 5.15.0\n", + "plotnine 0.12.4\n", + "pluggy 1.4.0\n", + "polars 0.20.2\n", + "pooch 1.8.0\n", + "portpicker 1.5.2\n", + "prefetch-generator 1.0.3\n", + "preshed 3.0.9\n", + "prettytable 3.9.0\n", + "proglog 0.1.10\n", + "progressbar2 4.2.0\n", + "prometheus-client 0.19.0\n", + "promise 2.3\n", + "prompt-toolkit 3.0.43\n", + "prophet 1.1.5\n", + "proto-plus 1.23.0\n", + "protobuf 3.20.3\n", + "psutil 5.9.5\n", + "psycopg2 2.9.9\n", + "ptyprocess 0.7.0\n", + "py-cpuinfo 9.0.0\n", + "py4j 0.10.9.7\n", + "pyarrow 10.0.1\n", + "pyarrow-hotfix 0.6\n", + "pyasn1 0.5.1\n", + "pyasn1-modules 0.3.0\n", + "pycocotools 2.0.7\n", + "pycparser 2.21\n", + "pyct 0.5.0\n", + "pydantic 1.10.14\n", + "pydata-google-auth 1.8.2\n", + "pydot 1.4.2\n", + "pydot-ng 2.0.0\n", + "pydotplus 2.0.2\n", + "PyDrive 1.3.1\n", + "PyDrive2 1.6.3\n", + "pyerfa 2.0.1.1\n", + "pygame 2.5.2\n", + "Pygments 2.16.1\n", + "PyGObject 3.42.1\n", + "PyJWT 2.3.0\n", + "pymc 5.7.2\n", + "pymystem3 0.2.0\n", + "PyOpenGL 3.1.7\n", + "pyOpenSSL 24.0.0\n", + "pyparsing 3.1.1\n", + "pyperclip 1.8.2\n", + "pyproj 3.6.1\n", + "pyproject_hooks 1.0.0\n", + "pyshp 2.3.1\n", + "PySocks 1.7.1\n", + "pytensor 2.14.2\n", + "pytest 7.4.4\n", + "python-apt 0.0.0\n", + "python-box 7.1.1\n", + "python-dateutil 2.8.2\n", + "python-louvain 0.16\n", + "python-slugify 8.0.3\n", + "python-utils 3.8.2\n", + "pytz 2023.4\n", + "pyviz_comms 3.0.1\n", + "PyWavelets 1.5.0\n", + "PyYAML 6.0.1\n", + "pyzmq 23.2.1\n", + "qdldl 0.1.7.post0\n", + "qudida 0.0.4\n", + "ratelim 0.1.6\n", + "referencing 0.33.0\n", + "regex 2023.12.25\n", + "requests 2.31.0\n", + "requests-oauthlib 1.3.1\n", + "requirements-parser 0.5.0\n", + "rich 13.7.0\n", + "rpds-py 0.17.1\n", + "rpy2 3.4.2\n", + "rsa 4.9\n", + "safetensors 0.4.2\n", + "scikit-image 0.19.3\n", + "scikit-learn 1.2.2\n", + "scipy 1.11.4\n", + "scooby 0.9.2\n", + "scs 3.2.4.post1\n", + "seaborn 0.13.1\n", + "SecretStorage 3.3.1\n", + "Send2Trash 1.8.2\n", + "sentencepiece 0.1.99\n", + "setuptools 67.7.2\n", + "shapely 2.0.2\n", + "six 1.16.0\n", + "sklearn-pandas 2.2.0\n", + "smart-open 6.4.0\n", + "sniffio 1.3.0\n", + "snowballstemmer 2.2.0\n", + "sortedcontainers 2.4.0\n", + "soundfile 0.12.1\n", + "soupsieve 2.5\n", + "soxr 0.3.7\n", + "spacy 3.7.2\n", + "spacy-legacy 3.0.12\n", + "spacy-loggers 1.0.5\n", + "Sphinx 5.0.2\n", + "sphinxcontrib-applehelp 1.0.8\n", + "sphinxcontrib-devhelp 1.0.6\n", + "sphinxcontrib-htmlhelp 2.0.5\n", + "sphinxcontrib-jsmath 1.0.1\n", + "sphinxcontrib-qthelp 1.0.7\n", + "sphinxcontrib-serializinghtml 1.1.10\n", + "SQLAlchemy 2.0.24\n", + "sqlglot 19.9.0\n", + "sqlparse 0.4.4\n", + "srsly 2.4.8\n", + "stanio 0.3.0\n", + "statsmodels 0.14.1\n", + "sympy 1.12\n", + "tables 3.8.0\n", + "tabulate 0.9.0\n", + "tbb 2021.11.0\n", + "tblib 3.0.0\n", + "tenacity 8.2.3\n", + "tensorboard 2.15.1\n", + "tensorboard-data-server 0.7.2\n", + "tensorflow 2.15.0\n", + "tensorflow-datasets 4.9.4\n", + "tensorflow-estimator 2.15.0\n", + "tensorflow-gcs-config 2.15.0\n", + "tensorflow-hub 0.16.1\n", + "tensorflow-io-gcs-filesystem 0.35.0\n", + "tensorflow-metadata 1.14.0\n", + "tensorflow-probability 0.22.0\n", + "tensorstore 0.1.45\n", + "termcolor 2.4.0\n", + "terminado 0.18.0\n", + "text-unidecode 1.3\n", + "textblob 0.17.1\n", + "tf-keras 2.15.0\n", + "tf-slim 1.1.0\n", + "thinc 8.2.2\n", + "threadpoolctl 3.2.0\n", + "tifffile 2024.1.30\n", + "tinycss2 1.2.1\n", + "tokenizers 0.15.1\n", + "toml 0.10.2\n", + "tomli 2.0.1\n", + "toolz 0.12.1\n", + "torch 2.1.0+cu121\n", + "torchaudio 2.1.0+cu121\n", + "torchdata 0.7.0\n", + "torchsummary 1.5.1\n", + "torchtext 0.16.0\n", + "torchvision 0.16.0+cu121\n", + "tornado 6.3.2\n", + "tqdm 4.66.1\n", + "traitlets 5.7.1\n", + "traittypes 0.2.1\n", + "transformers 4.35.2\n", + "triton 2.1.0\n", + "tweepy 4.14.0\n", + "typer 0.9.0\n", + "types-pytz 2023.4.0.20240130\n", + "types-setuptools 69.0.0.20240125\n", + "typing_extensions 4.5.0\n", + "tzlocal 5.2\n", + "uc-micro-py 1.0.2\n", + "uritemplate 4.1.1\n", + "urllib3 2.0.7\n", + "vega-datasets 0.9.0\n", + "wadllib 1.3.6\n", + "wasabi 1.1.2\n", + "wcwidth 0.2.13\n", + "weasel 0.3.4\n", + "webcolors 1.13\n", + "webencodings 0.5.1\n", + "websocket-client 1.7.0\n", + "Werkzeug 3.0.1\n", + "wheel 0.42.0\n", + "widgetsnbextension 3.6.6\n", + "wordcloud 1.9.3\n", + "wrapt 1.14.1\n", + "xarray 2023.7.0\n", + "xarray-einstats 0.7.0\n", + "xgboost 2.0.3\n", + "xlrd 2.0.1\n", + "xxhash 3.4.1\n", + "xyzservices 2023.10.1\n", + "yarl 1.9.4\n", + "yellowbrick 1.5\n", + "yfinance 0.2.36\n", + "zict 3.0.0\n", + "zipp 3.17.0\n" + ] + } + ], + "source": [ + "!pip list" + ] + }, + { + "cell_type": "code", + "source": [ + "!pip install git+https://gitlab.windenergy.dtu.dk/TOPFARM/edwin.git@dev#egg=ed_win[interarray]" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2eHfVnhvgWhH", + "outputId": "72464de1-5b1e-474e-863f-eb654177b425" + }, + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\u001b[33mDEPRECATION: git+https://gitlab.windenergy.dtu.dk/TOPFARM/edwin.git@dev#egg=ed_win[interarray] contains an egg fragment with a non-PEP 508 name pip 25.0 will enforce this behaviour change. A possible replacement is to use the req @ url syntax, and remove the egg fragment. Discussion can be found at https://github.com/pypa/pip/issues/11617\u001b[0m\u001b[33m\n", + "\u001b[0mCollecting ed_win[interarray]\n", + " Cloning https://gitlab.windenergy.dtu.dk/TOPFARM/edwin.git (to revision dev) to /tmp/pip-install-aa1oyns0/ed-win_a6fa5804473945488392bcae7422427a\n", + " Running command git clone --filter=blob:none --quiet https://gitlab.windenergy.dtu.dk/TOPFARM/edwin.git /tmp/pip-install-aa1oyns0/ed-win_a6fa5804473945488392bcae7422427a\n", + " Running command git checkout -b dev --track origin/dev\n", + " Switched to a new branch 'dev'\n", + " Branch 'dev' set up to track remote branch 'dev' from 'origin'.\n", + " Resolved https://gitlab.windenergy.dtu.dk/TOPFARM/edwin.git to commit 33d9d1abb7e1c68ad92f42856b8b266851ffdb97\n", + " Running command git submodule update --init --recursive -q\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/dist-packages (from ed_win[interarray]) (3.7.1)\n", + "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from ed_win[interarray]) (1.23.5)\n", + "Requirement already satisfied: xarray in /usr/local/lib/python3.10/dist-packages (from ed_win[interarray]) (2023.7.0)\n", + "Requirement already satisfied: scipy in /usr/local/lib/python3.10/dist-packages (from ed_win[interarray]) (1.11.4)\n", + "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from ed_win[interarray]) (3.2.1)\n", + "Requirement already satisfied: openpyxl in /usr/local/lib/python3.10/dist-packages (from ed_win[interarray]) (3.1.2)\n", + "Collecting pony (from ed_win[interarray])\n", + " Downloading pony-0.7.17-py3-none-any.whl (316 kB)\n", + "\u001b[2K \u001b[90mâ”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”\u001b[0m \u001b[32m316.7/316.7 kB\u001b[0m \u001b[31m2.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pyyaml in /usr/local/lib/python3.10/dist-packages (from ed_win[interarray]) (6.0.1)\n", + "Collecting utm (from ed_win[interarray])\n", + " Downloading utm-0.7.0.tar.gz (8.7 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: shapely in /usr/local/lib/python3.10/dist-packages (from ed_win[interarray]) (2.0.2)\n", + "Collecting pyomo (from ed_win[interarray])\n", + " Downloading Pyomo-6.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (12.5 MB)\n", + "\u001b[2K \u001b[90mâ”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”\u001b[0m \u001b[32m12.5/12.5 MB\u001b[0m \u001b[31m41.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting loguru (from ed_win[interarray])\n", + " Downloading loguru-0.7.2-py3-none-any.whl (62 kB)\n", + "\u001b[2K \u001b[90mâ”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”\u001b[0m \u001b[32m62.5/62.5 kB\u001b[0m \u001b[31m6.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: numba in /usr/local/lib/python3.10/dist-packages (from ed_win[interarray]) (0.58.1)\n", + "Collecting ortools (from ed_win[interarray])\n", + " Downloading ortools-9.8.3296-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (22.9 MB)\n", + "\u001b[2K \u001b[90mâ”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”\u001b[0m \u001b[32m22.9/22.9 MB\u001b[0m \u001b[31m31.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting svg.py (from ed_win[interarray])\n", + " Downloading svg.py-1.4.2-py3-none-any.whl (13 kB)\n", + "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->ed_win[interarray]) (1.2.0)\n", + "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib->ed_win[interarray]) (0.12.1)\n", + "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib->ed_win[interarray]) (4.47.2)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->ed_win[interarray]) (1.4.5)\n", + "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib->ed_win[interarray]) (23.2)\n", + "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib->ed_win[interarray]) (9.4.0)\n", + "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib->ed_win[interarray]) (3.1.1)\n", + "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib->ed_win[interarray]) (2.8.2)\n", + "Requirement already satisfied: llvmlite<0.42,>=0.41.0dev0 in /usr/local/lib/python3.10/dist-packages (from numba->ed_win[interarray]) (0.41.1)\n", + "Requirement already satisfied: et-xmlfile in /usr/local/lib/python3.10/dist-packages (from openpyxl->ed_win[interarray]) (1.1.0)\n", + "Collecting absl-py>=2.0.0 (from ortools->ed_win[interarray])\n", + " Downloading absl_py-2.1.0-py3-none-any.whl (133 kB)\n", + "\u001b[2K \u001b[90mâ”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”\u001b[0m \u001b[32m133.7/133.7 kB\u001b[0m \u001b[31m14.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting pandas>=2.0.0 (from ortools->ed_win[interarray])\n", + " Downloading pandas-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (13.0 MB)\n", + "\u001b[2K \u001b[90mâ”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”\u001b[0m \u001b[32m13.0/13.0 MB\u001b[0m \u001b[31m40.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting protobuf>=4.25.0 (from ortools->ed_win[interarray])\n", + " Downloading protobuf-4.25.2-cp37-abi3-manylinux2014_x86_64.whl (294 kB)\n", + "\u001b[2K \u001b[90mâ”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”\u001b[0m \u001b[32m294.6/294.6 kB\u001b[0m \u001b[31m31.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting ply (from pyomo->ed_win[interarray])\n", + " Downloading ply-3.11-py2.py3-none-any.whl (49 kB)\n", + "\u001b[2K \u001b[90mâ”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”\u001b[0m \u001b[32m49.6/49.6 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas>=2.0.0->ortools->ed_win[interarray]) (2023.4)\n", + "Collecting tzdata>=2022.7 (from pandas>=2.0.0->ortools->ed_win[interarray])\n", + " Downloading tzdata-2023.4-py2.py3-none-any.whl (346 kB)\n", + "\u001b[2K \u001b[90mâ”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”\u001b[0m \u001b[32m346.6/346.6 kB\u001b[0m \u001b[31m28.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib->ed_win[interarray]) (1.16.0)\n", + "Building wheels for collected packages: ed_win, utm\n", + " Building wheel for ed_win (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for ed_win: filename=ed_win-0.0.1-py3-none-any.whl size=137976 sha256=b801786f3f356b3d2289990ab8a8140180a24fd96ba804844b84854a8675f6a5\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-z4blk4km/wheels/bf/9b/a7/6f79573adc34ef490169f4d295af41c9e43a21e4e234845b61\n", + " Building wheel for utm (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for utm: filename=utm-0.7.0-py3-none-any.whl size=6084 sha256=a24f8a300c41d26e4c03959ca237d7063c43f08722f9748f491537cf26a05319\n", + " Stored in directory: /root/.cache/pip/wheels/2f/a1/c8/543df0e8f5e824c3e92a432e32deb9cd89ae686095ee8cfcbe\n", + "Successfully built ed_win utm\n", + "Installing collected packages: utm, pony, ply, tzdata, svg.py, pyomo, protobuf, loguru, absl-py, pandas, ortools, ed_win\n", + " Attempting uninstall: protobuf\n", + " Found existing installation: protobuf 3.20.3\n", + " Uninstalling protobuf-3.20.3:\n", + " Successfully uninstalled protobuf-3.20.3\n", + " Attempting uninstall: absl-py\n", + " Found existing installation: absl-py 1.4.0\n", + " Uninstalling absl-py-1.4.0:\n", + " Successfully uninstalled absl-py-1.4.0\n", + " Attempting uninstall: pandas\n", + " Found existing installation: pandas 1.5.3\n", + " Uninstalling pandas-1.5.3:\n", + " Successfully uninstalled pandas-1.5.3\n", + "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", + "lida 0.0.10 requires fastapi, which is not installed.\n", + "lida 0.0.10 requires kaleido, which is not installed.\n", + "lida 0.0.10 requires python-multipart, which is not installed.\n", + "lida 0.0.10 requires uvicorn, which is not installed.\n", + "bigframes 0.20.0 requires pandas<2.1.4,>=1.5.0, but you have pandas 2.2.0 which is incompatible.\n", + "google-colab 1.0.0 requires pandas==1.5.3, but you have pandas 2.2.0 which is incompatible.\n", + "tensorboard 2.15.1 requires protobuf<4.24,>=3.19.6, but you have protobuf 4.25.2 which is incompatible.\n", + "tensorflow-metadata 1.14.0 requires absl-py<2.0.0,>=0.9, but you have absl-py 2.1.0 which is incompatible.\n", + "tensorflow-metadata 1.14.0 requires protobuf<4.21,>=3.20.3, but you have protobuf 4.25.2 which is incompatible.\u001b[0m\u001b[31m\n", + "\u001b[0mSuccessfully installed absl-py-2.1.0 ed_win-0.0.1 loguru-0.7.2 ortools-9.8.3296 pandas-2.2.0 ply-3.11 pony-0.7.17 protobuf-4.25.2 pyomo-6.7.0 svg.py-1.4.2 tzdata-2023.4 utm-0.7.0\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "!pip install windIO" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "s2J13hr3hVyR", + "outputId": "e4372f8a-cf82-4d5d-d75a-6ceb1be64115" + }, + "execution_count": 3, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting windIO\n", + " Downloading windIO-1.0-py3-none-any.whl (9.9 kB)\n", + "Requirement already satisfied: jsonschema in /usr/local/lib/python3.10/dist-packages (from windIO) (4.19.2)\n", + "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from windIO) (1.23.5)\n", + "Requirement already satisfied: pyyaml in /usr/local/lib/python3.10/dist-packages (from windIO) (6.0.1)\n", + "Requirement already satisfied: xarray in /usr/local/lib/python3.10/dist-packages (from windIO) (2023.7.0)\n", + "Requirement already satisfied: attrs>=22.2.0 in /usr/local/lib/python3.10/dist-packages (from jsonschema->windIO) (23.2.0)\n", + "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /usr/local/lib/python3.10/dist-packages (from jsonschema->windIO) (2023.12.1)\n", + "Requirement already satisfied: referencing>=0.28.4 in /usr/local/lib/python3.10/dist-packages (from jsonschema->windIO) (0.33.0)\n", + "Requirement already satisfied: rpds-py>=0.7.1 in /usr/local/lib/python3.10/dist-packages (from jsonschema->windIO) (0.17.1)\n", + "Requirement already satisfied: pandas>=1.4 in /usr/local/lib/python3.10/dist-packages (from xarray->windIO) (2.2.0)\n", + "Requirement already satisfied: packaging>=21.3 in /usr/local/lib/python3.10/dist-packages (from xarray->windIO) (23.2)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.4->xarray->windIO) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.4->xarray->windIO) (2023.4)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.4->xarray->windIO) (2023.4)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.2->pandas>=1.4->xarray->windIO) (1.16.0)\n", + "Installing collected packages: windIO\n", + "Successfully installed windIO-1.0\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "!apt-get install -y -qq coinor-cbc" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0rnKFUJy2Aqw", + "outputId": "8cf0b252-1181-4f69-adcc-5e8c0e69d110" + }, + "execution_count": 4, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Selecting previously unselected package coinor-libcoinutils3v5:amd64.\n", + "(Reading database ... 121730 files and directories currently installed.)\n", + "Preparing to unpack .../0-coinor-libcoinutils3v5_2.11.4+repack1-2_amd64.deb ...\n", + "Unpacking coinor-libcoinutils3v5:amd64 (2.11.4+repack1-2) ...\n", + "Selecting previously unselected package coinor-libosi1v5:amd64.\n", + "Preparing to unpack .../1-coinor-libosi1v5_0.108.6+repack1-2_amd64.deb ...\n", + "Unpacking coinor-libosi1v5:amd64 (0.108.6+repack1-2) ...\n", + "Selecting previously unselected package coinor-libclp1.\n", + "Preparing to unpack .../2-coinor-libclp1_1.17.5+repack1-1_amd64.deb ...\n", + "Unpacking coinor-libclp1 (1.17.5+repack1-1) ...\n", + "Selecting previously unselected package coinor-libcgl1:amd64.\n", + "Preparing to unpack .../3-coinor-libcgl1_0.60.3+repack1-3_amd64.deb ...\n", + "Unpacking coinor-libcgl1:amd64 (0.60.3+repack1-3) ...\n", + "Selecting previously unselected package coinor-libcbc3:amd64.\n", + "Preparing to unpack .../4-coinor-libcbc3_2.10.7+ds1-1_amd64.deb ...\n", + "Unpacking coinor-libcbc3:amd64 (2.10.7+ds1-1) ...\n", + "Selecting previously unselected package coinor-cbc.\n", + "Preparing to unpack .../5-coinor-cbc_2.10.7+ds1-1_amd64.deb ...\n", + "Unpacking coinor-cbc (2.10.7+ds1-1) ...\n", + "Setting up coinor-libcoinutils3v5:amd64 (2.11.4+repack1-2) ...\n", + "Setting up coinor-libosi1v5:amd64 (0.108.6+repack1-2) ...\n", + "Setting up coinor-libclp1 (1.17.5+repack1-1) ...\n", + "Setting up coinor-libcgl1:amd64 (0.60.3+repack1-3) ...\n", + "Setting up coinor-libcbc3:amd64 (2.10.7+ds1-1) ...\n", + "Setting up coinor-cbc (2.10.7+ds1-1) ...\n", + "Processing triggers for man-db (2.10.2-1) ...\n", + "Processing triggers for libc-bin (2.35-0ubuntu3.4) ...\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbbbind.so.3 is not a symbolic link\n", + "\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbb.so.12 is not a symbolic link\n", + "\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbbbind_2_0.so.3 is not a symbolic link\n", + "\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbbmalloc.so.2 is not a symbolic link\n", + "\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbbbind_2_5.so.3 is not a symbolic link\n", + "\n", + "/sbin/ldconfig.real: /usr/local/lib/libtbbmalloc_proxy.so.2 is not a symbolic link\n", + "\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "from ed_win.wind_farm_network import WindFarmNetwork, InterArrayDriver\n", + "from windIO.utils.yml_utils import load_yaml, Loader\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import xarray as xr\n", + "import psutil\n", + "import os" + ], + "metadata": { + "id": "kD3cwAzbgl-2" + }, + "execution_count": 5, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Get memory information\n", + "memory_info = psutil.virtual_memory()\n", + "\n", + "# Print available memory\n", + "print(f\"Total Memory: {memory_info.total / (1024 ** 3):.2f} GB\")\n", + "print(f\"Available Memory: {memory_info.available / (1024 ** 3):.2f} GB\")\n", + "\n", + "# Get CPU count and usage\n", + "cpu_count = psutil.cpu_count(logical=False)\n", + "cpu_count_logical = psutil.cpu_count(logical=True)\n", + "cpu_percent = psutil.cpu_percent(interval=1)\n", + "\n", + "# Get CPU frequency\n", + "cpu_freq = psutil.cpu_freq()\n", + "\n", + "# Print the information\n", + "print(f\"Physical CPUs: {cpu_count}\\nLogical CPUs: {cpu_count_logical}\\nCPU Usage: {cpu_percent}%\")\n", + "print(f\"CPU Frequency: {cpu_freq.current} MHz\")" + ], + "metadata": { + "id": "yVDfw4CBiA4o", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "2678f9e7-5649-4648-80c5-d52ed4bb3da1" + }, + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Total Memory: 12.67 GB\n", + "Available Memory: 11.53 GB\n", + "Physical CPUs: 1\n", + "Logical CPUs: 2\n", + "CPU Usage: 4.0%\n", + "CPU Frequency: 2199.998 MHz\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Custom constructor for netCDF data\n", + "def includeBathymetryNetCDF(self, node):\n", + " filename = os.path.join(self._root, self.construct_scalar(node))\n", + " dataset = xr.open_dataset(filename)\n", + " bathymetry_data = {variable: list(dataset[variable].values.reshape(-1))\n", + " for variable in dataset.variables}\n", + " return bathymetry_data\n", + "\n", + "Loader.includeBathymetryNetCDF = includeBathymetryNetCDF\n", + "Loader.add_constructor('!includeBathymetryNetCDF',\n", + " Loader.includeBathymetryNetCDF)" + ], + "metadata": { + "id": "PbizrVzxlm1i" + }, + "execution_count": 7, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Data extracted from IEA37 Borssele load_data\n", + "substation_pos = np.asarray([[497620.7], [5730622.0]])\n", + "cable_costs = [206, 287, 406] # [€/m] Costs per distance for each cable type\n", + "turbines_per_cable = [3, 5, 7]\n", + "cross_section = [500, 1000, 1500]\n", + "BoundaryC = np.array(list(zip([484178.55, 500129.9, 497318.1,\n", + " 503163.37, 501266.5, 488951.0],\n", + " [5732482.8, 5737534.4, 5731880.24,\n", + " 5729155.3, 5715990.05, 5727940.])))" + ], + "metadata": { + "id": "9FvKCv8Dlqqm" + }, + "execution_count": 8, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "interarray_setting = {\n", + " 'solver_name': 'cbc', # choose between cplex, cbc,\n", + " 'gap': 0.002, # gab between best\n", + " 'timelimit': 2000, # seconds\n", + " 'other_settings': {\n", + " 'solver_io': 'python',\n", + " 'relax_boundary': True,\n", + " 'tee': True,\n", + " 'warmstart': True,\n", + " 'gateXings_constraint': True,\n", + " 'branching': True,\n", + " 'gates_limit': False,\n", + " 'timeMode': 'elapsed',\n", + " 'threads': len(psutil.Process().cpu_affinity()),\n", + " 'get_cbc_path': False,\n", + " 'cbc_solver_path': \"Probive path to cbc.exe if you want to use your local cbc\"\n", + " }\n", + "}" + ], + "metadata": { + "id": "bPBwDzkkl1pn" + }, + "execution_count": 9, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "cables = np.array([[cx, capacity, cost] for cx, capacity, cost in zip(cross_section, turbines_per_cable, cable_costs)])" + ], + "metadata": { + "id": "214a0Qzyl6NK" + }, + "execution_count": 10, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Turbine positions from IEA37 Borssele Regular System\n", + "turbine_pos = np.array([[ 500968.1461, 499748.6565, 501245.7744, 500026.2848,\n", + " 498527.8286, 497308.339 , 501523.4027, 500303.9131,\n", + " 498805.4569, 497585.9673, 496087.5111, 494868.0215,\n", + " 501801.031 , 500581.5414, 499083.0852, 497863.5956,\n", + " 496365.1394, 495145.6498, 493647.1936, 492427.704 ,\n", + " 502078.6593, 500859.1697, 499360.7135, 498141.2239,\n", + " 496642.7677, 495423.2781, 493924.8219, 492705.3323,\n", + " 491206.8762, 489987.3865, 502356.2876, 501136.798 ,\n", + " 499638.3418, 498418.8522, 496920.396 , 495700.9064,\n", + " 494202.4502, 492982.9606, 491484.5045, 490265.0148,\n", + " 488766.5587, 487547.069 , 502633.9159, 501414.4263,\n", + " 499915.9701, 498696.4805, 497198.0243, 495978.5347,\n", + " 494480.0786, 493260.5889, 491762.1328, 490542.6431,\n", + " 489044.187 , 487824.6973, 486326.2412, 485106.7515,\n", + " 497475.6526, 496256.163 , 494757.7069, 493538.2172,\n", + " 492039.7611, 490820.2714, 489321.8153, 488102.3256,\n", + " 486603.8695, 497753.2809, 496533.7913, 495035.3352,\n", + " 493815.8455, 492317.3894, 489599.4436, 498030.9093,\n", + " 496811.4196, 495312.9635],\n", + " [5716452.784 , 5717635.848 , 5718427.22 , 5719610.283 ,\n", + " 5718809.394 , 5719992.458 , 5720401.656 , 5721584.719 ,\n", + " 5720783.83 , 5721966.894 , 5721166.004 , 5722349.068 ,\n", + " 5722376.092 , 5723559.155 , 5722758.266 , 5723941.33 ,\n", + " 5723140.44 , 5724323.504 , 5723522.615 , 5724705.678 ,\n", + " 5724350.528 , 5725533.591 , 5724732.702 , 5725915.765 ,\n", + " 5725114.876 , 5726297.94 , 5725497.051 , 5726680.114 ,\n", + " 5725879.225 , 5727062.288 , 5726324.963 , 5727508.027 ,\n", + " 5726707.138 , 5727890.201 , 5727089.312 , 5728272.376 ,\n", + " 5727471.486 , 5728654.55 , 5727853.661 , 5729036.724 ,\n", + " 5728235.835 , 5729418.899 , 5728299.399 , 5729482.463 ,\n", + " 5728681.574 , 5729864.637 , 5729063.748 , 5730246.812 ,\n", + " 5729445.922 , 5730628.986 , 5729828.097 , 5731011.16 ,\n", + " 5730210.271 , 5731393.335 , 5730592.445 , 5731775.509 ,\n", + " 5731038.184 , 5732221.248 , 5731420.358 , 5732603.422 ,\n", + " 5731802.533 , 5732985.596 , 5732184.707 , 5733367.77 ,\n", + " 5732566.881 , 5733012.62 , 5734195.683 , 5733394.794 ,\n", + " 5734577.858 , 5733776.968 , 5734159.143 , 5734987.056 ,\n", + " 5736170.119 , 5735369.23 ]])" + ], + "metadata": { + "id": "ZLCWLKQ6l-gx" + }, + "execution_count": 11, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "site_info = {'site_name': 'IEA-37 Regular',\n", + " 'handle': 'iea37reg',\n", + " 'boundary': BoundaryC\n", + " }" + ], + "metadata": { + "id": "Aw_axaoxrAv1" + }, + "execution_count": 12, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "wfn = WindFarmNetwork(turbine_positions=turbine_pos,\n", + " substation_positions=substation_pos,\n", + " drivers=[InterArrayDriver(**interarray_setting)],\n", + " sequence=[0],\n", + " cables=cables,\n", + " site_info=site_info)" + ], + "metadata": { + "id": "Nky0X180pMBu" + }, + "execution_count": 13, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "cost, state = wfn.design()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "kTgZqZM8rEfV", + "outputId": "8d7e3f5b-2aed-49ab-8aba-aedfb89bb2ce" + }, + "execution_count": 14, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The solver is available\n", + "Solving \"IEA-37 Regular\", k = k\n", + "\n", + "Welcome to the CBC MILP Solver \n", + "Version: 2.10.7 \n", + "Build Date: Feb 14 2022 \n", + "\n", + "command line - /usr/bin/cbc -ratio 0.002 -seconds 2000 -timeMode elapsed -threads 2 -printingOptions all -import /tmp/tmpmw2j3bwt.pyomo.lp -mipstart /tmp/tmpi93_6cgt.cbc.soln -stat=1 -solve -solu /tmp/tmpmw2j3bwt.pyomo.soln (default strategy 1)\n", + "ratioGap was changed from 0 to 0.002\n", + "seconds was changed from 1e+100 to 2000\n", + "Option for timeMode changed from cpu to elapsed\n", + "threads was changed from 0 to 2\n", + "Option for printingOptions changed from normal to all\n", + "opening mipstart file /tmp/tmpi93_6cgt.cbc.soln.\n", + "MIPStart values read for 148 variables.\n", + "Presolve 3582 (-77) rows, 1514 (-2) columns and 11680 (-828) elements\n", + "Statistics for presolved model\n", + "Original problem has 1516 integers (758 of which binary)\n", + "Presolved problem has 1514 integers (756 of which binary)\n", + "==== 758 zero objective 184 different\n", + "==== absolute objective values 184 different\n", + "==== for integers 758 zero objective 184 different\n", + "==== for integers absolute objective values 184 different\n", + "===== end objective counts\n", + "\n", + "\n", + "Problem has 3582 rows, 1514 columns (756 with objective) and 11680 elements\n", + "Column breakdown:\n", + "0 of type 0.0->inf, 758 of type 0.0->up, 0 of type lo->inf, \n", + "0 of type lo->up, 0 of type free, 0 of type fixed, \n", + "0 of type -inf->0.0, 0 of type -inf->up, 756 of type 0.0->1.0 \n", + "Row breakdown:\n", + "0 of type E 0.0, 146 of type E 1.0, 0 of type E -1.0, \n", + "1 of type E other, 0 of type G 0.0, 0 of type G 1.0, \n", + "1 of type G other, 1514 of type L 0.0, 1842 of type L 1.0, \n", + "78 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other, \n", + "0 of type Free \n", + "Continuous objective value is 134213 - 0.06 seconds\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 1437 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 1157 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 592 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 202 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 31 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 14 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 2 strengthened rows, 0 substitutions\n", + "Cgl0004I processed model has 3095 rows, 1516 columns (1516 integer (758 of which binary)) and 13291 elements\n", + "Cbc0045I MIPStart provided solution with cost 152084\n", + "Cbc0012I Integer solution of 152083.98 found by Reduced search after 0 iterations and 0 nodes (0.60 seconds)\n", + "Cbc0038I Full problem 3095 rows 1516 columns, reduced to 310 rows 166 columns\n", + "Cbc0031I 76 added rows had average density of 191.18421\n", + "Cbc0013I At root node, 76 cuts changed objective from 135593.25 to 136600.82 in 15 passes\n", + "Cbc0014I Cut generator 0 (Probing) - 4 row cuts average 11.5 elements, 0 column cuts (0 active) in 0.225 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 1 (Gomory) - 289 row cuts average 435.4 elements, 0 column cuts (0 active) in 0.503 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 2 (Knapsack) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.041 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.007 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.114 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 5 (FlowCover) - 143 row cuts average 13.1 elements, 0 column cuts (0 active) in 0.044 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 6 (TwoMirCuts) - 307 row cuts average 186.7 elements, 0 column cuts (0 active) in 0.209 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 7 (ZeroHalf) - 20 row cuts average 12.9 elements, 0 column cuts (0 active) in 0.264 seconds - new frequency is -100\n", + "Cbc0010I After 0 nodes, 1 on tree, 152083.98 best solution, best possible 136600.82 (3.36 seconds)\n", + "Cbc0010I After 100 nodes, 62 on tree, 152083.98 best solution, best possible 136609.87 (12.45 seconds)\n", + "Cbc0010I After 200 nodes, 113 on tree, 152083.98 best solution, best possible 136609.87 (13.36 seconds)\n", + "Cbc0010I After 300 nodes, 145 on tree, 152083.98 best solution, best possible 136609.87 (13.89 seconds)\n", + "Cbc0012I Integer solution of 151904.79 found by heuristic after 24078 iterations and 332 nodes (14.38 seconds)\n", + "Cbc0010I After 400 nodes, 182 on tree, 151904.79 best solution, best possible 136609.87 (15.17 seconds)\n", + "Cbc0010I After 500 nodes, 233 on tree, 151904.79 best solution, best possible 136609.87 (17.13 seconds)\n", + "Cbc0010I After 600 nodes, 284 on tree, 151904.79 best solution, best possible 136609.87 (18.81 seconds)\n", + "Cbc0010I After 700 nodes, 333 on tree, 151904.79 best solution, best possible 136609.87 (19.96 seconds)\n", + "Cbc0010I After 800 nodes, 380 on tree, 151904.79 best solution, best possible 136609.87 (21.52 seconds)\n", + "Cbc0010I After 900 nodes, 428 on tree, 151904.79 best solution, best possible 136609.87 (22.89 seconds)\n", + "Cbc0010I After 1000 nodes, 475 on tree, 151904.79 best solution, best possible 136609.87 (24.27 seconds)\n", + "Cbc0010I After 1100 nodes, 519 on tree, 151904.79 best solution, best possible 136609.87 (25.84 seconds)\n", + "Cbc0010I After 1200 nodes, 567 on tree, 151904.79 best solution, best possible 136609.87 (27.12 seconds)\n", + "Cbc0010I After 1300 nodes, 613 on tree, 151904.79 best solution, best possible 136609.87 (28.42 seconds)\n", + "Cbc0010I After 1400 nodes, 662 on tree, 151904.79 best solution, best possible 136609.87 (30.90 seconds)\n", + "Cbc0010I After 1500 nodes, 707 on tree, 151904.79 best solution, best possible 136609.87 (32.28 seconds)\n", + "Cbc0010I After 1600 nodes, 757 on tree, 151904.79 best solution, best possible 136609.87 (33.56 seconds)\n", + "Cbc0010I After 1700 nodes, 806 on tree, 151904.79 best solution, best possible 136609.87 (34.52 seconds)\n", + "Cbc0010I After 1800 nodes, 854 on tree, 151904.79 best solution, best possible 136609.87 (35.47 seconds)\n", + "Cbc0010I After 1900 nodes, 903 on tree, 151904.79 best solution, best possible 136609.87 (36.50 seconds)\n", + "Cbc0010I After 2000 nodes, 954 on tree, 151904.79 best solution, best possible 136609.87 (37.52 seconds)\n", + "Cbc0010I After 2100 nodes, 999 on tree, 151904.79 best solution, best possible 136609.87 (38.69 seconds)\n", + "Cbc0010I After 2200 nodes, 1048 on tree, 151904.79 best solution, best possible 136609.87 (39.77 seconds)\n", + "Cbc0010I After 2300 nodes, 1097 on tree, 151904.79 best solution, best possible 136609.87 (40.62 seconds)\n", + "Cbc0010I After 2400 nodes, 1144 on tree, 151904.79 best solution, best possible 136609.87 (41.44 seconds)\n", + "Cbc0010I After 2500 nodes, 1184 on tree, 151904.79 best solution, best possible 136609.87 (42.74 seconds)\n", + "Cbc0010I After 2600 nodes, 1232 on tree, 151904.79 best solution, best possible 136609.87 (44.20 seconds)\n", + "Cbc0010I After 2700 nodes, 1273 on tree, 151904.79 best solution, best possible 136609.87 (44.97 seconds)\n", + "Cbc0010I After 2800 nodes, 1321 on tree, 151904.79 best solution, best possible 136609.87 (45.88 seconds)\n", + "Cbc0012I Integer solution of 148676.72 found by heuristic after 147519 iterations and 2838 nodes (46.16 seconds)\n", + "Cbc0012I Integer solution of 144826.46 found by heuristic after 150115 iterations and 2867 nodes (47.60 seconds)\n", + "Cbc0010I After 2900 nodes, 1050 on tree, 144826.46 best solution, best possible 136609.87 (49.50 seconds)\n", + "Cbc0010I After 3000 nodes, 1102 on tree, 144826.46 best solution, best possible 136609.87 (53.39 seconds)\n", + "Cbc0010I After 3100 nodes, 1150 on tree, 144826.46 best solution, best possible 136609.87 (55.82 seconds)\n", + "Cbc0010I After 3200 nodes, 1201 on tree, 144826.46 best solution, best possible 136609.87 (58.83 seconds)\n", + "Cbc0010I After 3300 nodes, 1250 on tree, 144826.46 best solution, best possible 136609.87 (61.45 seconds)\n", + "Cbc0010I After 3400 nodes, 1301 on tree, 144826.46 best solution, best possible 136609.87 (63.21 seconds)\n", + "Cbc0010I After 3500 nodes, 1350 on tree, 144826.46 best solution, best possible 136609.87 (64.90 seconds)\n", + "Cbc0010I After 3600 nodes, 1397 on tree, 144826.46 best solution, best possible 136609.87 (66.20 seconds)\n", + "Cbc0010I After 3700 nodes, 1448 on tree, 144826.46 best solution, best possible 136609.87 (67.98 seconds)\n", + "Cbc0010I After 3800 nodes, 1499 on tree, 144826.46 best solution, best possible 136609.87 (70.25 seconds)\n", + "Cbc0010I After 3900 nodes, 1549 on tree, 144826.46 best solution, best possible 136609.87 (71.28 seconds)\n", + "Cbc0010I After 4000 nodes, 1597 on tree, 144826.46 best solution, best possible 136609.87 (72.23 seconds)\n", + "Cbc0010I After 4100 nodes, 1643 on tree, 144826.46 best solution, best possible 136609.87 (73.25 seconds)\n", + "Cbc0010I After 4200 nodes, 1691 on tree, 144826.46 best solution, best possible 136609.87 (74.64 seconds)\n", + "Cbc0010I After 4300 nodes, 1742 on tree, 144826.46 best solution, best possible 136609.87 (76.21 seconds)\n", + "Cbc0010I After 4400 nodes, 1788 on tree, 144826.46 best solution, best possible 136609.87 (77.84 seconds)\n", + "Cbc0010I After 4500 nodes, 1838 on tree, 144826.46 best solution, best possible 136609.87 (79.12 seconds)\n", + "Cbc0010I After 4600 nodes, 1886 on tree, 144826.46 best solution, best possible 136609.87 (80.37 seconds)\n", + "Cbc0010I After 4700 nodes, 1937 on tree, 144826.46 best solution, best possible 136609.87 (82.75 seconds)\n", + "Cbc0010I After 4800 nodes, 1984 on tree, 144826.46 best solution, best possible 136609.87 (84.05 seconds)\n", + "Cbc0010I After 4900 nodes, 2031 on tree, 144826.46 best solution, best possible 136609.87 (85.01 seconds)\n", + "Cbc0010I After 5000 nodes, 2082 on tree, 144826.46 best solution, best possible 136609.87 (86.44 seconds)\n", + "Cbc0010I After 5100 nodes, 2133 on tree, 144826.46 best solution, best possible 136609.87 (87.65 seconds)\n", + "Cbc0010I After 5200 nodes, 2182 on tree, 144826.46 best solution, best possible 136609.87 (88.79 seconds)\n", + "Cbc0010I After 5300 nodes, 2227 on tree, 144826.46 best solution, best possible 136609.87 (90.09 seconds)\n", + "Cbc0010I After 5400 nodes, 2275 on tree, 144826.46 best solution, best possible 136609.87 (91.51 seconds)\n", + "Cbc0010I After 5500 nodes, 2324 on tree, 144826.46 best solution, best possible 136609.87 (92.62 seconds)\n", + "Cbc0010I After 5600 nodes, 2371 on tree, 144826.46 best solution, best possible 136609.87 (94.40 seconds)\n", + "Cbc0010I After 5700 nodes, 2420 on tree, 144826.46 best solution, best possible 136609.87 (96.55 seconds)\n", + "Cbc0010I After 5800 nodes, 2471 on tree, 144826.46 best solution, best possible 136609.87 (97.89 seconds)\n", + "Cbc0010I After 5900 nodes, 2520 on tree, 144826.46 best solution, best possible 136609.87 (98.75 seconds)\n", + "Cbc0010I After 6000 nodes, 2567 on tree, 144826.46 best solution, best possible 136609.87 (99.77 seconds)\n", + "Cbc0010I After 6100 nodes, 2617 on tree, 144826.46 best solution, best possible 136609.87 (101.05 seconds)\n", + "Cbc0010I After 6200 nodes, 2666 on tree, 144826.46 best solution, best possible 136609.87 (102.30 seconds)\n", + "Cbc0010I After 6300 nodes, 2715 on tree, 144826.46 best solution, best possible 136609.87 (103.57 seconds)\n", + "Cbc0010I After 6400 nodes, 2763 on tree, 144826.46 best solution, best possible 136609.87 (104.76 seconds)\n", + "Cbc0010I After 6500 nodes, 2813 on tree, 144826.46 best solution, best possible 136609.87 (105.94 seconds)\n", + "Cbc0010I After 6600 nodes, 2858 on tree, 144826.46 best solution, best possible 136609.87 (107.37 seconds)\n", + "Cbc0010I After 6700 nodes, 2905 on tree, 144826.46 best solution, best possible 136609.87 (109.21 seconds)\n", + "Cbc0010I After 6800 nodes, 2955 on tree, 144826.46 best solution, best possible 136609.87 (110.36 seconds)\n", + "Cbc0010I After 6900 nodes, 3002 on tree, 144826.46 best solution, best possible 136609.87 (111.90 seconds)\n", + "Cbc0010I After 7000 nodes, 3051 on tree, 144826.46 best solution, best possible 136609.87 (112.79 seconds)\n", + "Cbc0010I After 7100 nodes, 3096 on tree, 144826.46 best solution, best possible 136609.87 (113.76 seconds)\n", + "Cbc0010I After 7200 nodes, 3142 on tree, 144826.46 best solution, best possible 136609.87 (114.86 seconds)\n", + "Cbc0010I After 7300 nodes, 3188 on tree, 144826.46 best solution, best possible 136609.87 (116.23 seconds)\n", + "Cbc0010I After 7400 nodes, 3236 on tree, 144826.46 best solution, best possible 136609.87 (117.54 seconds)\n", + "Cbc0010I After 7500 nodes, 3286 on tree, 144826.46 best solution, best possible 136609.87 (118.31 seconds)\n", + "Cbc0010I After 7600 nodes, 3331 on tree, 144826.46 best solution, best possible 136609.87 (119.38 seconds)\n", + "Cbc0010I After 7700 nodes, 3375 on tree, 144826.46 best solution, best possible 136609.87 (120.95 seconds)\n", + "Cbc0010I After 7800 nodes, 3413 on tree, 144826.46 best solution, best possible 136609.87 (122.20 seconds)\n", + "Cbc0010I After 7900 nodes, 3450 on tree, 144826.46 best solution, best possible 136609.87 (123.25 seconds)\n", + "Cbc0010I After 8000 nodes, 3498 on tree, 144826.46 best solution, best possible 136609.87 (124.54 seconds)\n", + "Cbc0010I After 8100 nodes, 3547 on tree, 144826.46 best solution, best possible 136609.87 (125.70 seconds)\n", + "Cbc0010I After 8200 nodes, 3591 on tree, 144826.46 best solution, best possible 136609.87 (127.22 seconds)\n", + "Cbc0010I After 8300 nodes, 3638 on tree, 144826.46 best solution, best possible 136609.87 (128.19 seconds)\n", + "Cbc0010I After 8400 nodes, 3685 on tree, 144826.46 best solution, best possible 136609.87 (128.97 seconds)\n", + "Cbc0010I After 8500 nodes, 3734 on tree, 144826.46 best solution, best possible 136609.87 (130.04 seconds)\n", + "Cbc0010I After 8600 nodes, 3781 on tree, 144826.46 best solution, best possible 136609.87 (130.94 seconds)\n", + "Cbc0010I After 8700 nodes, 3826 on tree, 144826.46 best solution, best possible 136609.87 (132.10 seconds)\n", + "Cbc0010I After 8800 nodes, 3873 on tree, 144826.46 best solution, best possible 136609.87 (133.79 seconds)\n", + "Cbc0010I After 8900 nodes, 3917 on tree, 144826.46 best solution, best possible 136609.87 (134.96 seconds)\n", + "Cbc0010I After 9000 nodes, 3966 on tree, 144826.46 best solution, best possible 136609.87 (136.07 seconds)\n", + "Cbc0010I After 9100 nodes, 4013 on tree, 144826.46 best solution, best possible 136609.87 (136.86 seconds)\n", + "Cbc0010I After 9200 nodes, 4060 on tree, 144826.46 best solution, best possible 136609.87 (137.75 seconds)\n", + "Cbc0010I After 9300 nodes, 4104 on tree, 144826.46 best solution, best possible 136609.87 (138.52 seconds)\n", + "Cbc0010I After 9400 nodes, 4154 on tree, 144826.46 best solution, best possible 136609.87 (139.67 seconds)\n", + "Cbc0010I After 9500 nodes, 4203 on tree, 144826.46 best solution, best possible 136609.87 (141.03 seconds)\n", + "Cbc0010I After 9600 nodes, 4251 on tree, 144826.46 best solution, best possible 136609.87 (141.80 seconds)\n", + "Cbc0010I After 9700 nodes, 4295 on tree, 144826.46 best solution, best possible 136609.87 (142.40 seconds)\n", + "Cbc0010I After 9800 nodes, 4337 on tree, 144826.46 best solution, best possible 136609.87 (142.93 seconds)\n", + "Cbc0010I After 9900 nodes, 4374 on tree, 144826.46 best solution, best possible 136609.87 (143.52 seconds)\n", + "Cbc0010I After 10000 nodes, 4419 on tree, 144826.46 best solution, best possible 136609.87 (144.42 seconds)\n", + "Cbc0010I After 10100 nodes, 4462 on tree, 144826.46 best solution, best possible 136609.87 (146.69 seconds)\n", + "Cbc0010I After 10200 nodes, 4509 on tree, 144826.46 best solution, best possible 136609.87 (148.21 seconds)\n", + "Cbc0010I After 10300 nodes, 4557 on tree, 144826.46 best solution, best possible 136609.87 (149.46 seconds)\n", + "Cbc0010I After 10400 nodes, 4607 on tree, 144826.46 best solution, best possible 136609.87 (150.44 seconds)\n", + "Cbc0010I After 10500 nodes, 4650 on tree, 144826.46 best solution, best possible 136609.87 (151.58 seconds)\n", + "Cbc0010I After 10600 nodes, 4700 on tree, 144826.46 best solution, best possible 136609.87 (152.69 seconds)\n", + "Cbc0010I After 10700 nodes, 4743 on tree, 144826.46 best solution, best possible 136609.87 (153.66 seconds)\n", + "Cbc0010I After 10800 nodes, 4794 on tree, 144826.46 best solution, best possible 136609.87 (154.59 seconds)\n", + "Cbc0010I After 10900 nodes, 4845 on tree, 144826.46 best solution, best possible 136609.87 (155.36 seconds)\n", + "Cbc0010I After 11000 nodes, 4884 on tree, 144826.46 best solution, best possible 136609.87 (156.00 seconds)\n", + "Cbc0010I After 11100 nodes, 4933 on tree, 144826.46 best solution, best possible 136869.63 (168.24 seconds)\n", + "Cbc0010I After 11200 nodes, 4981 on tree, 144826.46 best solution, best possible 136923.61 (173.95 seconds)\n", + "Cbc0010I After 11300 nodes, 5030 on tree, 144826.46 best solution, best possible 136973.19 (178.27 seconds)\n", + "Cbc0010I After 11400 nodes, 5081 on tree, 144826.46 best solution, best possible 136999.49 (181.23 seconds)\n", + "Cbc0010I After 11500 nodes, 5131 on tree, 144826.46 best solution, best possible 137020.47 (185.02 seconds)\n", + "Cbc0010I After 11600 nodes, 5181 on tree, 144826.46 best solution, best possible 137041.23 (189.14 seconds)\n", + "Cbc0010I After 11700 nodes, 5231 on tree, 144826.46 best solution, best possible 137059.69 (192.08 seconds)\n", + "Cbc0010I After 11800 nodes, 5281 on tree, 144826.46 best solution, best possible 137076.39 (195.15 seconds)\n", + "Cbc0010I After 11900 nodes, 5330 on tree, 144826.46 best solution, best possible 137089.58 (199.15 seconds)\n", + "Cbc0010I After 12000 nodes, 5380 on tree, 144826.46 best solution, best possible 137101.44 (202.22 seconds)\n", + "Cbc0010I After 12100 nodes, 5431 on tree, 144826.46 best solution, best possible 137113.69 (204.73 seconds)\n", + "Cbc0010I After 12200 nodes, 5481 on tree, 144826.46 best solution, best possible 137122.87 (207.44 seconds)\n", + "Cbc0010I After 12300 nodes, 5531 on tree, 144826.46 best solution, best possible 137132.52 (211.13 seconds)\n", + "Cbc0010I After 12400 nodes, 5581 on tree, 144826.46 best solution, best possible 137144.8 (214.68 seconds)\n", + "Cbc0010I After 12500 nodes, 5630 on tree, 144826.46 best solution, best possible 137151.13 (217.04 seconds)\n", + "Cbc0010I After 12600 nodes, 5680 on tree, 144826.46 best solution, best possible 137159.02 (219.04 seconds)\n", + "Cbc0010I After 12700 nodes, 5730 on tree, 144826.46 best solution, best possible 137167.7 (221.46 seconds)\n", + "Cbc0010I After 12800 nodes, 5780 on tree, 144826.46 best solution, best possible 137175.97 (224.79 seconds)\n", + "Cbc0010I After 12900 nodes, 5830 on tree, 144826.46 best solution, best possible 137182.67 (227.74 seconds)\n", + "Cbc0010I After 13000 nodes, 5880 on tree, 144826.46 best solution, best possible 137189.66 (230.23 seconds)\n", + "Cbc0010I After 13100 nodes, 5931 on tree, 144826.46 best solution, best possible 137189.66 (232.84 seconds)\n", + "Cbc0010I After 13200 nodes, 5982 on tree, 144826.46 best solution, best possible 137189.66 (234.63 seconds)\n", + "Cbc0010I After 13300 nodes, 6032 on tree, 144826.46 best solution, best possible 137189.66 (237.70 seconds)\n", + "Cbc0010I After 13400 nodes, 6081 on tree, 144826.46 best solution, best possible 137189.66 (240.00 seconds)\n", + "Cbc0010I After 13500 nodes, 6132 on tree, 144826.46 best solution, best possible 137189.66 (241.90 seconds)\n", + "Cbc0010I After 13600 nodes, 6181 on tree, 144826.46 best solution, best possible 137189.66 (243.89 seconds)\n", + "Cbc0010I After 13700 nodes, 6231 on tree, 144826.46 best solution, best possible 137189.66 (245.77 seconds)\n", + "Cbc0010I After 13800 nodes, 6283 on tree, 144826.46 best solution, best possible 137189.66 (247.76 seconds)\n", + "Cbc0010I After 13900 nodes, 6331 on tree, 144826.46 best solution, best possible 137189.66 (250.71 seconds)\n", + "Cbc0010I After 14000 nodes, 6381 on tree, 144826.46 best solution, best possible 137189.66 (252.83 seconds)\n", + "Cbc0010I After 14100 nodes, 6431 on tree, 144826.46 best solution, best possible 137196.24 (255.68 seconds)\n", + "Cbc0010I After 14200 nodes, 6480 on tree, 144826.46 best solution, best possible 137204.46 (258.12 seconds)\n", + "Cbc0010I After 14300 nodes, 6531 on tree, 144826.46 best solution, best possible 137209.59 (260.79 seconds)\n", + "Cbc0010I After 14400 nodes, 6581 on tree, 144826.46 best solution, best possible 137215.27 (264.12 seconds)\n", + "Cbc0010I After 14500 nodes, 6630 on tree, 144826.46 best solution, best possible 137220.29 (266.56 seconds)\n", + "Cbc0010I After 14600 nodes, 6681 on tree, 144826.46 best solution, best possible 137225.44 (269.38 seconds)\n", + "Cbc0010I After 14700 nodes, 6730 on tree, 144826.46 best solution, best possible 137231.42 (271.82 seconds)\n", + "Cbc0010I After 14800 nodes, 6780 on tree, 144826.46 best solution, best possible 137236.85 (274.91 seconds)\n", + "Cbc0010I After 14900 nodes, 6831 on tree, 144826.46 best solution, best possible 137240.95 (277.52 seconds)\n", + "Cbc0010I After 15000 nodes, 6880 on tree, 144826.46 best solution, best possible 137246.39 (279.89 seconds)\n", + "Cbc0010I After 15100 nodes, 6930 on tree, 144826.46 best solution, best possible 137250.23 (282.48 seconds)\n", + "Cbc0010I After 15200 nodes, 6981 on tree, 144826.46 best solution, best possible 137254.69 (284.84 seconds)\n", + "Cbc0010I After 15300 nodes, 7031 on tree, 144826.46 best solution, best possible 137259.44 (287.48 seconds)\n", + "Cbc0010I After 15400 nodes, 7081 on tree, 144826.46 best solution, best possible 137263.42 (290.70 seconds)\n", + "Cbc0010I After 15500 nodes, 7131 on tree, 144826.46 best solution, best possible 137267.66 (293.03 seconds)\n", + "Cbc0010I After 15600 nodes, 7181 on tree, 144826.46 best solution, best possible 137272.13 (295.63 seconds)\n", + "Cbc0010I After 15700 nodes, 7231 on tree, 144826.46 best solution, best possible 137276.55 (297.60 seconds)\n", + "Cbc0010I After 15800 nodes, 7281 on tree, 144826.46 best solution, best possible 137281.48 (301.02 seconds)\n", + "Cbc0010I After 15900 nodes, 7330 on tree, 144826.46 best solution, best possible 137285.92 (303.87 seconds)\n", + "Cbc0010I After 16000 nodes, 7380 on tree, 144826.46 best solution, best possible 137290 (305.94 seconds)\n", + "Cbc0010I After 16100 nodes, 7431 on tree, 144826.46 best solution, best possible 137294.26 (308.23 seconds)\n", + "Cbc0010I After 16200 nodes, 7479 on tree, 144826.46 best solution, best possible 137297.78 (310.64 seconds)\n", + "Cbc0010I After 16300 nodes, 7530 on tree, 144826.46 best solution, best possible 137300.12 (312.76 seconds)\n", + "Cbc0010I After 16400 nodes, 7579 on tree, 144826.46 best solution, best possible 137303.58 (316.20 seconds)\n", + "Cbc0010I After 16500 nodes, 7629 on tree, 144826.46 best solution, best possible 137307.12 (318.49 seconds)\n", + "Cbc0010I After 16600 nodes, 7679 on tree, 144826.46 best solution, best possible 137310.89 (320.43 seconds)\n", + "Cbc0010I After 16700 nodes, 7729 on tree, 144826.46 best solution, best possible 137314.97 (322.64 seconds)\n", + "Cbc0010I After 16800 nodes, 7780 on tree, 144826.46 best solution, best possible 137317.57 (324.67 seconds)\n", + "Cbc0010I After 16900 nodes, 7829 on tree, 144826.46 best solution, best possible 137320.7 (328.31 seconds)\n", + "Cbc0010I After 17000 nodes, 7879 on tree, 144826.46 best solution, best possible 137323.66 (331.02 seconds)\n", + "Cbc0010I After 17100 nodes, 7930 on tree, 144826.46 best solution, best possible 137323.66 (333.11 seconds)\n", + "Cbc0010I After 17200 nodes, 7979 on tree, 144826.46 best solution, best possible 137323.66 (334.84 seconds)\n", + "Cbc0010I After 17300 nodes, 8029 on tree, 144826.46 best solution, best possible 137323.66 (336.58 seconds)\n", + "Cbc0010I After 17400 nodes, 8079 on tree, 144826.46 best solution, best possible 137323.66 (338.38 seconds)\n", + "Cbc0010I After 17500 nodes, 8129 on tree, 144826.46 best solution, best possible 137323.66 (341.13 seconds)\n", + "Cbc0010I After 17600 nodes, 8179 on tree, 144826.46 best solution, best possible 137323.66 (343.08 seconds)\n", + "Cbc0010I After 17700 nodes, 8230 on tree, 144826.46 best solution, best possible 137323.66 (344.81 seconds)\n", + "Cbc0010I After 17800 nodes, 8280 on tree, 144826.46 best solution, best possible 137323.66 (346.56 seconds)\n", + "Cbc0010I After 17900 nodes, 8330 on tree, 144826.46 best solution, best possible 137323.66 (348.19 seconds)\n", + "Cbc0010I After 18000 nodes, 8380 on tree, 144826.46 best solution, best possible 137323.66 (349.79 seconds)\n", + "Cbc0010I After 18100 nodes, 8430 on tree, 144826.46 best solution, best possible 137326.43 (352.89 seconds)\n", + "Cbc0010I After 18200 nodes, 8481 on tree, 144826.46 best solution, best possible 137329.14 (355.34 seconds)\n", + "Cbc0010I After 18300 nodes, 8531 on tree, 144826.46 best solution, best possible 137332.81 (357.46 seconds)\n", + "Cbc0010I After 18400 nodes, 8581 on tree, 144826.46 best solution, best possible 137335.71 (359.51 seconds)\n", + "Cbc0010I After 18500 nodes, 8631 on tree, 144826.46 best solution, best possible 137339.55 (361.60 seconds)\n", + "Cbc0010I After 18600 nodes, 8680 on tree, 144826.46 best solution, best possible 137342.87 (363.82 seconds)\n", + "Cbc0010I After 18700 nodes, 8731 on tree, 144826.46 best solution, best possible 137345.04 (367.17 seconds)\n", + "Cbc0010I After 18800 nodes, 8780 on tree, 144826.46 best solution, best possible 137347.95 (369.09 seconds)\n", + "Cbc0010I After 18900 nodes, 8831 on tree, 144826.46 best solution, best possible 137350.5 (371.32 seconds)\n", + "Cbc0010I After 19000 nodes, 8880 on tree, 144826.46 best solution, best possible 137352.91 (373.72 seconds)\n", + "Cbc0010I After 19100 nodes, 8931 on tree, 144826.46 best solution, best possible 137355.55 (375.83 seconds)\n", + "Cbc0010I After 19200 nodes, 8980 on tree, 144826.46 best solution, best possible 137357.86 (378.79 seconds)\n", + "Cbc0010I After 19300 nodes, 9031 on tree, 144826.46 best solution, best possible 137359.98 (380.86 seconds)\n", + "Cbc0010I After 19400 nodes, 9080 on tree, 144826.46 best solution, best possible 137362.23 (382.84 seconds)\n", + "Cbc0010I After 19500 nodes, 9130 on tree, 144826.46 best solution, best possible 137364.6 (385.18 seconds)\n", + "Cbc0010I After 19600 nodes, 9181 on tree, 144826.46 best solution, best possible 137366.69 (387.33 seconds)\n", + "Cbc0010I After 19700 nodes, 9230 on tree, 144826.46 best solution, best possible 137369.25 (389.31 seconds)\n", + "Cbc0010I After 19800 nodes, 9280 on tree, 144826.46 best solution, best possible 137371.99 (393.04 seconds)\n", + "Cbc0010I After 19900 nodes, 9331 on tree, 144826.46 best solution, best possible 137374.48 (395.34 seconds)\n", + "Cbc0010I After 20000 nodes, 9380 on tree, 144826.46 best solution, best possible 137376.75 (397.44 seconds)\n", + "Cbc0010I After 20100 nodes, 9431 on tree, 144826.46 best solution, best possible 137378.78 (399.61 seconds)\n", + "Cbc0010I After 20200 nodes, 9480 on tree, 144826.46 best solution, best possible 137380.79 (401.82 seconds)\n", + "Cbc0010I After 20300 nodes, 9531 on tree, 144826.46 best solution, best possible 137382.62 (405.28 seconds)\n", + "Cbc0010I After 20400 nodes, 9580 on tree, 144826.46 best solution, best possible 137384.59 (407.51 seconds)\n", + "Cbc0010I After 20500 nodes, 9631 on tree, 144826.46 best solution, best possible 137387.83 (409.84 seconds)\n", + "Cbc0010I After 20600 nodes, 9681 on tree, 144826.46 best solution, best possible 137390.25 (412.19 seconds)\n", + "Cbc0010I After 20700 nodes, 9731 on tree, 144826.46 best solution, best possible 137392.68 (414.43 seconds)\n", + "Cbc0010I After 20800 nodes, 9781 on tree, 144826.46 best solution, best possible 137395.69 (417.84 seconds)\n", + "Cbc0010I After 20900 nodes, 9831 on tree, 144826.46 best solution, best possible 137398.06 (419.94 seconds)\n", + "Cbc0010I After 21000 nodes, 9880 on tree, 144826.46 best solution, best possible 137400.77 (422.15 seconds)\n", + "Cbc0010I After 21100 nodes, 9931 on tree, 144826.46 best solution, best possible 137400.77 (423.62 seconds)\n", + "Cbc0010I After 21200 nodes, 9981 on tree, 144826.46 best solution, best possible 137400.77 (425.23 seconds)\n", + "Cbc0010I After 21300 nodes, 10030 on tree, 144826.46 best solution, best possible 137400.77 (426.45 seconds)\n", + "Cbc0010I After 21400 nodes, 10080 on tree, 144826.46 best solution, best possible 137400.77 (427.72 seconds)\n", + "Cbc0010I After 21500 nodes, 10128 on tree, 144826.46 best solution, best possible 137400.77 (430.61 seconds)\n", + "Cbc0010I After 21600 nodes, 10179 on tree, 144826.46 best solution, best possible 137400.77 (432.23 seconds)\n", + "Cbc0010I After 21700 nodes, 10227 on tree, 144826.46 best solution, best possible 137400.77 (433.75 seconds)\n", + "Cbc0010I After 21800 nodes, 10276 on tree, 144826.46 best solution, best possible 137400.77 (435.56 seconds)\n", + "Cbc0010I After 21900 nodes, 10327 on tree, 144826.46 best solution, best possible 137400.77 (437.43 seconds)\n", + "Cbc0010I After 22000 nodes, 10376 on tree, 144826.46 best solution, best possible 137400.77 (439.16 seconds)\n", + "Cbc0010I After 22100 nodes, 10426 on tree, 144826.46 best solution, best possible 137400.77 (440.87 seconds)\n", + "Cbc0010I After 22200 nodes, 10475 on tree, 144826.46 best solution, best possible 137400.77 (443.73 seconds)\n", + "Cbc0010I After 22300 nodes, 10526 on tree, 144826.46 best solution, best possible 137400.77 (445.38 seconds)\n", + "Cbc0010I After 22400 nodes, 10574 on tree, 144826.46 best solution, best possible 137400.77 (446.48 seconds)\n", + "Cbc0010I After 22500 nodes, 10620 on tree, 144826.46 best solution, best possible 137400.77 (447.64 seconds)\n", + "Cbc0010I After 22600 nodes, 10670 on tree, 144826.46 best solution, best possible 137400.77 (449.30 seconds)\n", + "Cbc0010I After 22700 nodes, 10715 on tree, 144826.46 best solution, best possible 137400.77 (450.46 seconds)\n", + "Cbc0010I After 22800 nodes, 10765 on tree, 144826.46 best solution, best possible 137400.77 (452.09 seconds)\n", + "Cbc0010I After 22900 nodes, 10815 on tree, 144826.46 best solution, best possible 137400.77 (453.85 seconds)\n", + "Cbc0010I After 23000 nodes, 10864 on tree, 144826.46 best solution, best possible 137400.77 (456.62 seconds)\n", + "Cbc0010I After 23100 nodes, 10915 on tree, 144826.46 best solution, best possible 137400.77 (458.04 seconds)\n", + "Cbc0010I After 23200 nodes, 10964 on tree, 144826.46 best solution, best possible 137400.77 (459.39 seconds)\n", + "Cbc0010I After 23300 nodes, 11014 on tree, 144826.46 best solution, best possible 137400.77 (460.83 seconds)\n", + "Cbc0010I After 23400 nodes, 11064 on tree, 144826.46 best solution, best possible 137400.77 (462.53 seconds)\n", + "Cbc0010I After 23500 nodes, 11112 on tree, 144826.46 best solution, best possible 137400.77 (464.10 seconds)\n", + "Cbc0010I After 23600 nodes, 11163 on tree, 144826.46 best solution, best possible 137400.77 (465.64 seconds)\n", + "Cbc0010I After 23700 nodes, 11212 on tree, 144826.46 best solution, best possible 137400.77 (467.61 seconds)\n", + "Cbc0010I After 23800 nodes, 11262 on tree, 144826.46 best solution, best possible 137400.77 (469.77 seconds)\n", + "Cbc0010I After 23900 nodes, 11313 on tree, 144826.46 best solution, best possible 137400.77 (471.13 seconds)\n", + "Cbc0012I Integer solution of 140987.35 found by heuristic after 1798000 iterations and 23989 nodes (472.06 seconds)\n", + "Cbc0010I After 24000 nodes, 8167 on tree, 140987.35 best solution, best possible 137400.77 (472.38 seconds)\n", + "Cbc0010I After 24100 nodes, 8217 on tree, 140987.35 best solution, best possible 137403.07 (474.26 seconds)\n", + "Cbc0010I After 24200 nodes, 8266 on tree, 140987.35 best solution, best possible 137405.72 (476.12 seconds)\n", + "Cbc0010I After 24300 nodes, 8317 on tree, 140987.35 best solution, best possible 137407.79 (478.36 seconds)\n", + "Cbc0010I After 24400 nodes, 8366 on tree, 140987.35 best solution, best possible 137410.38 (481.64 seconds)\n", + "Cbc0010I After 24500 nodes, 8417 on tree, 140987.35 best solution, best possible 137412.78 (483.67 seconds)\n", + "Cbc0010I After 24600 nodes, 8466 on tree, 140987.35 best solution, best possible 137414.99 (485.83 seconds)\n", + "Cbc0010I After 24700 nodes, 8517 on tree, 140987.35 best solution, best possible 137417.28 (488.30 seconds)\n", + "Cbc0010I After 24800 nodes, 8567 on tree, 140987.35 best solution, best possible 137419.8 (490.23 seconds)\n", + "Cbc0010I After 24900 nodes, 8617 on tree, 140987.35 best solution, best possible 137422.33 (492.82 seconds)\n", + "Cbc0010I After 25000 nodes, 8666 on tree, 140987.35 best solution, best possible 137424.65 (495.87 seconds)\n", + "Cbc0010I After 25100 nodes, 8717 on tree, 140987.35 best solution, best possible 137424.65 (497.88 seconds)\n", + "Cbc0010I After 25200 nodes, 8768 on tree, 140987.35 best solution, best possible 137424.65 (499.39 seconds)\n", + "Cbc0010I After 25300 nodes, 8817 on tree, 140987.35 best solution, best possible 137424.65 (501.04 seconds)\n", + "Cbc0010I After 25400 nodes, 8867 on tree, 140987.35 best solution, best possible 137424.65 (502.81 seconds)\n", + "Cbc0010I After 25500 nodes, 8917 on tree, 140987.35 best solution, best possible 137424.65 (504.65 seconds)\n", + "Cbc0010I After 25600 nodes, 8967 on tree, 140987.35 best solution, best possible 137424.65 (507.23 seconds)\n", + "Cbc0010I After 25700 nodes, 9017 on tree, 140987.35 best solution, best possible 137424.65 (509.25 seconds)\n", + "Cbc0010I After 25800 nodes, 9066 on tree, 140987.35 best solution, best possible 137424.65 (510.97 seconds)\n", + "Cbc0010I After 25900 nodes, 9116 on tree, 140987.35 best solution, best possible 137424.65 (513.11 seconds)\n", + "Cbc0010I After 26000 nodes, 9166 on tree, 140987.35 best solution, best possible 137424.65 (515.18 seconds)\n", + "Cbc0010I After 26100 nodes, 9216 on tree, 140987.35 best solution, best possible 137426.97 (517.20 seconds)\n", + "Cbc0010I After 26200 nodes, 9265 on tree, 140987.35 best solution, best possible 137429.5 (520.38 seconds)\n", + "Cbc0010I After 26300 nodes, 9315 on tree, 140987.35 best solution, best possible 137430.92 (522.52 seconds)\n", + "Cbc0010I After 26400 nodes, 9365 on tree, 140987.35 best solution, best possible 137433.38 (524.99 seconds)\n", + "Cbc0010I After 26500 nodes, 9414 on tree, 140987.35 best solution, best possible 137436.07 (527.18 seconds)\n", + "Cbc0010I After 26600 nodes, 9465 on tree, 140987.35 best solution, best possible 137438.69 (529.21 seconds)\n", + "Cbc0010I After 26700 nodes, 9514 on tree, 140987.35 best solution, best possible 137440.91 (531.01 seconds)\n", + "Cbc0010I After 26800 nodes, 9564 on tree, 140987.35 best solution, best possible 137443.82 (534.39 seconds)\n", + "Cbc0010I After 26900 nodes, 9614 on tree, 140987.35 best solution, best possible 137446.8 (536.53 seconds)\n", + "Cbc0010I After 27000 nodes, 9665 on tree, 140987.35 best solution, best possible 137448.62 (538.62 seconds)\n", + "Cbc0010I After 27100 nodes, 9715 on tree, 140987.35 best solution, best possible 137450.99 (540.59 seconds)\n", + "Cbc0010I After 27200 nodes, 9765 on tree, 140987.35 best solution, best possible 137453.4 (542.89 seconds)\n", + "Cbc0010I After 27300 nodes, 9815 on tree, 140987.35 best solution, best possible 137455.72 (545.69 seconds)\n", + "Cbc0010I After 27400 nodes, 9864 on tree, 140987.35 best solution, best possible 137457.91 (547.77 seconds)\n", + "Cbc0010I After 27500 nodes, 9915 on tree, 140987.35 best solution, best possible 137460.24 (549.58 seconds)\n", + "Cbc0010I After 27600 nodes, 9965 on tree, 140987.35 best solution, best possible 137462.11 (551.64 seconds)\n", + "Cbc0010I After 27700 nodes, 10015 on tree, 140987.35 best solution, best possible 137464.05 (553.52 seconds)\n", + "Cbc0010I After 27800 nodes, 10065 on tree, 140987.35 best solution, best possible 137466.06 (555.38 seconds)\n", + "Cbc0010I After 27900 nodes, 10114 on tree, 140987.35 best solution, best possible 137468.01 (558.20 seconds)\n", + "Cbc0010I After 28000 nodes, 10164 on tree, 140987.35 best solution, best possible 137469.95 (560.70 seconds)\n", + "Cbc0010I After 28100 nodes, 10144 on tree, 140987.35 best solution, best possible 137469.95 (561.30 seconds)\n", + "Cbc0010I After 28200 nodes, 10146 on tree, 140987.35 best solution, best possible 137469.95 (561.94 seconds)\n", + "Cbc0010I After 28300 nodes, 10137 on tree, 140987.35 best solution, best possible 137469.95 (562.54 seconds)\n", + "Cbc0010I After 28400 nodes, 10134 on tree, 140987.35 best solution, best possible 137469.95 (563.05 seconds)\n", + "Cbc0010I After 28500 nodes, 10136 on tree, 140987.35 best solution, best possible 137469.95 (563.74 seconds)\n", + "Cbc0010I After 28600 nodes, 10137 on tree, 140987.35 best solution, best possible 137469.95 (564.24 seconds)\n", + "Cbc0010I After 28700 nodes, 10120 on tree, 140987.35 best solution, best possible 137469.95 (564.79 seconds)\n", + "Cbc0010I After 28800 nodes, 10118 on tree, 140987.35 best solution, best possible 137469.95 (565.27 seconds)\n", + "Cbc0010I After 28900 nodes, 10102 on tree, 140987.35 best solution, best possible 137469.95 (565.81 seconds)\n", + "Cbc0010I After 29000 nodes, 10104 on tree, 140987.35 best solution, best possible 137469.95 (566.52 seconds)\n", + "Cbc0010I After 29100 nodes, 10152 on tree, 140987.35 best solution, best possible 137472.12 (568.45 seconds)\n", + "Cbc0010I After 29200 nodes, 10203 on tree, 140987.35 best solution, best possible 137473.67 (571.57 seconds)\n", + "Cbc0010I After 29300 nodes, 10252 on tree, 140987.35 best solution, best possible 137475.83 (573.90 seconds)\n", + "Cbc0010I After 29400 nodes, 10302 on tree, 140987.35 best solution, best possible 137477.55 (575.63 seconds)\n", + "Cbc0010I After 29500 nodes, 10353 on tree, 140987.35 best solution, best possible 137479.47 (577.73 seconds)\n", + "Cbc0010I After 29600 nodes, 10402 on tree, 140987.35 best solution, best possible 137481.23 (580.25 seconds)\n", + "Cbc0010I After 29700 nodes, 10453 on tree, 140987.35 best solution, best possible 137483.22 (583.07 seconds)\n", + "Cbc0010I After 29800 nodes, 10503 on tree, 140987.35 best solution, best possible 137485.28 (585.80 seconds)\n", + "Cbc0010I After 29900 nodes, 10553 on tree, 140987.35 best solution, best possible 137486.92 (587.87 seconds)\n", + "Cbc0010I After 30000 nodes, 10603 on tree, 140987.35 best solution, best possible 137488.73 (590.02 seconds)\n", + "Cbc0010I After 30100 nodes, 10654 on tree, 140987.35 best solution, best possible 137488.75 (591.69 seconds)\n", + "Cbc0010I After 30200 nodes, 10705 on tree, 140987.35 best solution, best possible 137488.75 (593.34 seconds)\n", + "Cbc0010I After 30300 nodes, 10754 on tree, 140987.35 best solution, best possible 137488.75 (594.82 seconds)\n", + "Cbc0010I After 30400 nodes, 10803 on tree, 140987.35 best solution, best possible 137488.75 (597.58 seconds)\n", + "Cbc0010I After 30500 nodes, 10854 on tree, 140987.35 best solution, best possible 137488.75 (599.60 seconds)\n", + "Cbc0010I After 30600 nodes, 10903 on tree, 140987.35 best solution, best possible 137488.75 (601.15 seconds)\n", + "Cbc0010I After 30700 nodes, 10954 on tree, 140987.35 best solution, best possible 137488.75 (602.75 seconds)\n", + "Cbc0010I After 30800 nodes, 11003 on tree, 140987.35 best solution, best possible 137488.75 (604.14 seconds)\n", + "Cbc0010I After 30900 nodes, 11053 on tree, 140987.35 best solution, best possible 137488.75 (605.96 seconds)\n", + "Cbc0010I After 31000 nodes, 11104 on tree, 140987.35 best solution, best possible 137488.75 (607.73 seconds)\n", + "Cbc0010I After 31100 nodes, 11154 on tree, 140987.35 best solution, best possible 137488.75 (610.15 seconds)\n", + "Cbc0010I After 31200 nodes, 11204 on tree, 140987.35 best solution, best possible 137488.75 (612.30 seconds)\n", + "Cbc0010I After 31300 nodes, 11253 on tree, 140987.35 best solution, best possible 137488.75 (614.21 seconds)\n", + "Cbc0010I After 31400 nodes, 11304 on tree, 140987.35 best solution, best possible 137488.75 (615.58 seconds)\n", + "Cbc0010I After 31500 nodes, 11347 on tree, 140987.35 best solution, best possible 137488.75 (616.44 seconds)\n", + "Cbc0010I After 31600 nodes, 11393 on tree, 140987.35 best solution, best possible 137488.75 (617.26 seconds)\n", + "Cbc0010I After 31700 nodes, 11441 on tree, 140987.35 best solution, best possible 137488.75 (618.16 seconds)\n", + "Cbc0010I After 31800 nodes, 11490 on tree, 140987.35 best solution, best possible 137488.75 (618.95 seconds)\n", + "Cbc0010I After 31900 nodes, 11539 on tree, 140987.35 best solution, best possible 137488.75 (620.58 seconds)\n", + "Cbc0010I After 32000 nodes, 11587 on tree, 140987.35 best solution, best possible 137488.75 (623.24 seconds)\n", + "Cbc0010I After 32100 nodes, 11605 on tree, 140987.35 best solution, best possible 137488.75 (624.26 seconds)\n", + "Cbc0010I After 32200 nodes, 11616 on tree, 140987.35 best solution, best possible 137488.75 (624.93 seconds)\n", + "Cbc0010I After 32300 nodes, 11624 on tree, 140987.35 best solution, best possible 137488.75 (625.77 seconds)\n", + "Cbc0010I After 32400 nodes, 11638 on tree, 140987.35 best solution, best possible 137488.75 (626.33 seconds)\n", + "Cbc0010I After 32500 nodes, 11659 on tree, 140987.35 best solution, best possible 137488.75 (627.01 seconds)\n", + "Cbc0010I After 32600 nodes, 11663 on tree, 140987.35 best solution, best possible 137488.75 (627.84 seconds)\n", + "Cbc0010I After 32700 nodes, 11677 on tree, 140987.35 best solution, best possible 137488.75 (628.64 seconds)\n", + "Cbc0010I After 32800 nodes, 11677 on tree, 140987.35 best solution, best possible 137488.75 (629.33 seconds)\n", + "Cbc0010I After 32900 nodes, 11676 on tree, 140987.35 best solution, best possible 137488.75 (629.98 seconds)\n", + "Cbc0010I After 33000 nodes, 11670 on tree, 140987.35 best solution, best possible 137488.75 (630.62 seconds)\n", + "Cbc0010I After 33100 nodes, 11720 on tree, 140987.35 best solution, best possible 137490.37 (632.66 seconds)\n", + "Cbc0010I After 33200 nodes, 11771 on tree, 140987.35 best solution, best possible 137491.99 (635.26 seconds)\n", + "Cbc0010I After 33300 nodes, 11821 on tree, 140987.35 best solution, best possible 137494.01 (638.17 seconds)\n", + "Cbc0010I After 33400 nodes, 11871 on tree, 140987.35 best solution, best possible 137495.96 (640.13 seconds)\n", + "Cbc0010I After 33500 nodes, 11920 on tree, 140987.35 best solution, best possible 137497.49 (642.22 seconds)\n", + "Cbc0010I After 33600 nodes, 11971 on tree, 140987.35 best solution, best possible 137499.38 (644.35 seconds)\n", + "Cbc0010I After 33700 nodes, 12021 on tree, 140987.35 best solution, best possible 137501.07 (646.32 seconds)\n", + "Cbc0010I After 33800 nodes, 12070 on tree, 140987.35 best solution, best possible 137502.63 (648.98 seconds)\n", + "Cbc0010I After 33900 nodes, 12119 on tree, 140987.35 best solution, best possible 137503.98 (652.10 seconds)\n", + "Cbc0010I After 34000 nodes, 12169 on tree, 140987.35 best solution, best possible 137505.56 (654.16 seconds)\n", + "Cbc0010I After 34100 nodes, 12219 on tree, 140987.35 best solution, best possible 137505.58 (655.71 seconds)\n", + "Cbc0010I After 34200 nodes, 12269 on tree, 140987.35 best solution, best possible 137505.58 (657.64 seconds)\n", + "Cbc0010I After 34300 nodes, 12319 on tree, 140987.35 best solution, best possible 137505.58 (659.16 seconds)\n", + "Cbc0010I After 34400 nodes, 12368 on tree, 140987.35 best solution, best possible 137505.58 (660.83 seconds)\n", + "Cbc0010I After 34500 nodes, 12418 on tree, 140987.35 best solution, best possible 137505.58 (662.87 seconds)\n", + "Cbc0010I After 34600 nodes, 12466 on tree, 140987.35 best solution, best possible 137505.58 (664.05 seconds)\n", + "Cbc0010I After 34700 nodes, 12516 on tree, 140987.35 best solution, best possible 137505.58 (665.59 seconds)\n", + "Cbc0010I After 34800 nodes, 12565 on tree, 140987.35 best solution, best possible 137505.58 (667.30 seconds)\n", + "Cbc0010I After 34900 nodes, 12616 on tree, 140987.35 best solution, best possible 137505.58 (669.17 seconds)\n", + "Cbc0010I After 35000 nodes, 12667 on tree, 140987.35 best solution, best possible 137505.58 (670.67 seconds)\n", + "Cbc0010I After 35100 nodes, 12712 on tree, 140987.35 best solution, best possible 137505.58 (671.75 seconds)\n", + "Cbc0010I After 35200 nodes, 12762 on tree, 140987.35 best solution, best possible 137505.58 (673.69 seconds)\n", + "Cbc0010I After 35300 nodes, 12812 on tree, 140987.35 best solution, best possible 137505.58 (676.21 seconds)\n", + "Cbc0010I After 35400 nodes, 12861 on tree, 140987.35 best solution, best possible 137505.58 (678.20 seconds)\n", + "Cbc0010I After 35500 nodes, 12912 on tree, 140987.35 best solution, best possible 137505.58 (679.71 seconds)\n", + "Cbc0010I After 35600 nodes, 12961 on tree, 140987.35 best solution, best possible 137505.58 (681.41 seconds)\n", + "Cbc0010I After 35700 nodes, 13009 on tree, 140987.35 best solution, best possible 137505.58 (683.12 seconds)\n", + "Cbc0010I After 35800 nodes, 13059 on tree, 140987.35 best solution, best possible 137505.58 (684.64 seconds)\n", + "Cbc0010I After 35900 nodes, 13108 on tree, 140987.35 best solution, best possible 137505.58 (686.78 seconds)\n", + "Cbc0010I After 36000 nodes, 13156 on tree, 140987.35 best solution, best possible 137505.58 (689.31 seconds)\n", + "Cbc0010I After 36100 nodes, 13169 on tree, 140987.35 best solution, best possible 137505.58 (689.86 seconds)\n", + "Cbc0010I After 36200 nodes, 13175 on tree, 140987.35 best solution, best possible 137505.58 (690.51 seconds)\n", + "Cbc0010I After 36300 nodes, 13191 on tree, 140987.35 best solution, best possible 137505.58 (691.12 seconds)\n", + "Cbc0010I After 36400 nodes, 13204 on tree, 140987.35 best solution, best possible 137505.58 (691.86 seconds)\n", + "Cbc0010I After 36500 nodes, 13201 on tree, 140987.35 best solution, best possible 137505.58 (692.52 seconds)\n", + "Cbc0010I After 36600 nodes, 13196 on tree, 140987.35 best solution, best possible 137505.58 (693.23 seconds)\n", + "Cbc0010I After 36700 nodes, 13201 on tree, 140987.35 best solution, best possible 137505.58 (694.02 seconds)\n", + "Cbc0010I After 36800 nodes, 13207 on tree, 140987.35 best solution, best possible 137505.58 (694.64 seconds)\n", + "Cbc0010I After 36900 nodes, 13210 on tree, 140987.35 best solution, best possible 137505.58 (695.36 seconds)\n", + "Cbc0010I After 37000 nodes, 13206 on tree, 140987.35 best solution, best possible 137505.58 (696.01 seconds)\n", + "Cbc0010I After 37100 nodes, 13256 on tree, 140987.35 best solution, best possible 137507.05 (698.19 seconds)\n", + "Cbc0010I After 37200 nodes, 13305 on tree, 140987.35 best solution, best possible 137509.04 (700.41 seconds)\n", + "Cbc0010I After 37300 nodes, 13356 on tree, 140987.35 best solution, best possible 137510.58 (702.59 seconds)\n", + "Cbc0010I After 37400 nodes, 13405 on tree, 140987.35 best solution, best possible 137512.16 (704.25 seconds)\n", + "Cbc0010I After 37500 nodes, 13455 on tree, 140987.35 best solution, best possible 137514.1 (705.90 seconds)\n", + "Cbc0010I After 37600 nodes, 13505 on tree, 140987.35 best solution, best possible 137515.44 (707.84 seconds)\n", + "Cbc0010I After 37700 nodes, 13555 on tree, 140987.35 best solution, best possible 137517.47 (709.91 seconds)\n", + "Cbc0010I After 37800 nodes, 13605 on tree, 140987.35 best solution, best possible 137519.42 (712.50 seconds)\n", + "Cbc0010I After 37900 nodes, 13655 on tree, 140987.35 best solution, best possible 137521.28 (715.17 seconds)\n", + "Cbc0010I After 38000 nodes, 13705 on tree, 140987.35 best solution, best possible 137522.91 (717.10 seconds)\n", + "Cbc0010I After 38100 nodes, 13757 on tree, 140987.35 best solution, best possible 137522.91 (718.75 seconds)\n", + "Cbc0010I After 38200 nodes, 13805 on tree, 140987.35 best solution, best possible 137522.91 (720.40 seconds)\n", + "Cbc0010I After 38300 nodes, 13856 on tree, 140987.35 best solution, best possible 137522.91 (721.95 seconds)\n", + "Cbc0010I After 38400 nodes, 13905 on tree, 140987.35 best solution, best possible 137522.91 (723.54 seconds)\n", + "Cbc0010I After 38500 nodes, 13956 on tree, 140987.35 best solution, best possible 137522.91 (725.46 seconds)\n", + "Cbc0010I After 38600 nodes, 13999 on tree, 140987.35 best solution, best possible 137522.91 (727.55 seconds)\n", + "Cbc0010I After 38700 nodes, 14048 on tree, 140987.35 best solution, best possible 137522.91 (729.00 seconds)\n", + "Cbc0010I After 38800 nodes, 14093 on tree, 140987.35 best solution, best possible 137522.91 (730.03 seconds)\n", + "Cbc0010I After 38900 nodes, 14141 on tree, 140987.35 best solution, best possible 137522.91 (731.27 seconds)\n", + "Cbc0010I After 39000 nodes, 14189 on tree, 140987.35 best solution, best possible 137522.91 (733.08 seconds)\n", + "Cbc0010I After 39100 nodes, 14185 on tree, 140987.35 best solution, best possible 137522.91 (733.71 seconds)\n", + "Cbc0010I After 39200 nodes, 14165 on tree, 140987.35 best solution, best possible 137522.91 (734.27 seconds)\n", + "Cbc0010I After 39300 nodes, 14159 on tree, 140987.35 best solution, best possible 137522.91 (734.83 seconds)\n", + "Cbc0010I After 39400 nodes, 14163 on tree, 140987.35 best solution, best possible 137522.91 (735.35 seconds)\n", + "Cbc0010I After 39500 nodes, 14136 on tree, 140987.35 best solution, best possible 137522.91 (735.83 seconds)\n", + "Cbc0010I After 39600 nodes, 14132 on tree, 140987.35 best solution, best possible 137522.91 (736.51 seconds)\n", + "Cbc0010I After 39700 nodes, 14108 on tree, 140987.35 best solution, best possible 137522.91 (737.09 seconds)\n", + "Cbc0010I After 39800 nodes, 14109 on tree, 140987.35 best solution, best possible 137522.91 (738.06 seconds)\n", + "Cbc0010I After 39900 nodes, 14127 on tree, 140987.35 best solution, best possible 137522.91 (739.54 seconds)\n", + "Cbc0010I After 40000 nodes, 14117 on tree, 140987.35 best solution, best possible 137522.91 (740.50 seconds)\n", + "Cbc0010I After 40100 nodes, 14113 on tree, 140987.35 best solution, best possible 137522.91 (741.33 seconds)\n", + "Cbc0010I After 40200 nodes, 14112 on tree, 140987.35 best solution, best possible 137522.91 (742.08 seconds)\n", + "Cbc0010I After 40300 nodes, 14112 on tree, 140987.35 best solution, best possible 137522.91 (742.69 seconds)\n", + "Cbc0010I After 40400 nodes, 14086 on tree, 140987.35 best solution, best possible 137522.91 (743.33 seconds)\n", + "Cbc0010I After 40500 nodes, 14083 on tree, 140987.35 best solution, best possible 137522.91 (743.95 seconds)\n", + "Cbc0010I After 40600 nodes, 14084 on tree, 140987.35 best solution, best possible 137522.91 (744.49 seconds)\n", + "Cbc0010I After 40700 nodes, 14081 on tree, 140987.35 best solution, best possible 137522.91 (745.11 seconds)\n", + "Cbc0010I After 40800 nodes, 14091 on tree, 140987.35 best solution, best possible 137522.91 (745.87 seconds)\n", + "Cbc0010I After 40900 nodes, 14079 on tree, 140987.35 best solution, best possible 137522.91 (746.63 seconds)\n", + "Cbc0010I After 41000 nodes, 14082 on tree, 140987.35 best solution, best possible 137522.91 (747.32 seconds)\n", + "Cbc0010I After 41100 nodes, 14059 on tree, 140987.35 best solution, best possible 137522.91 (747.88 seconds)\n", + "Cbc0010I After 41200 nodes, 14058 on tree, 140987.35 best solution, best possible 137522.91 (748.55 seconds)\n", + "Cbc0010I After 41300 nodes, 14053 on tree, 140987.35 best solution, best possible 137522.91 (749.38 seconds)\n", + "Cbc0010I After 41400 nodes, 14050 on tree, 140987.35 best solution, best possible 137522.91 (749.97 seconds)\n", + "Cbc0010I After 41500 nodes, 14015 on tree, 140987.35 best solution, best possible 137522.91 (751.05 seconds)\n", + "Cbc0010I After 41600 nodes, 14009 on tree, 140987.35 best solution, best possible 137522.91 (752.22 seconds)\n", + "Cbc0010I After 41700 nodes, 14007 on tree, 140987.35 best solution, best possible 137522.91 (753.37 seconds)\n", + "Cbc0010I After 41800 nodes, 14010 on tree, 140987.35 best solution, best possible 137522.91 (754.24 seconds)\n", + "Cbc0010I After 41900 nodes, 14011 on tree, 140987.35 best solution, best possible 137522.91 (755.13 seconds)\n", + "Cbc0010I After 42000 nodes, 14003 on tree, 140987.35 best solution, best possible 137522.91 (755.92 seconds)\n", + "Cbc0010I After 42100 nodes, 13969 on tree, 140987.35 best solution, best possible 137522.91 (756.66 seconds)\n", + "Cbc0010I After 42200 nodes, 13967 on tree, 140987.35 best solution, best possible 137522.91 (757.35 seconds)\n", + "Cbc0010I After 42300 nodes, 13973 on tree, 140987.35 best solution, best possible 137522.91 (757.94 seconds)\n", + "Cbc0010I After 42400 nodes, 13969 on tree, 140987.35 best solution, best possible 137522.91 (758.59 seconds)\n", + "Cbc0010I After 42500 nodes, 13965 on tree, 140987.35 best solution, best possible 137522.91 (759.35 seconds)\n", + "Cbc0010I After 42600 nodes, 13981 on tree, 140987.35 best solution, best possible 137522.91 (760.23 seconds)\n", + "Cbc0010I After 42700 nodes, 13971 on tree, 140987.35 best solution, best possible 137522.91 (760.78 seconds)\n", + "Cbc0010I After 42800 nodes, 13963 on tree, 140987.35 best solution, best possible 137522.91 (761.65 seconds)\n", + "Cbc0010I After 42900 nodes, 13955 on tree, 140987.35 best solution, best possible 137522.91 (762.33 seconds)\n", + "Cbc0010I After 43000 nodes, 13938 on tree, 140987.35 best solution, best possible 137522.91 (763.34 seconds)\n", + "Cbc0010I After 43100 nodes, 13923 on tree, 140987.35 best solution, best possible 137522.91 (764.39 seconds)\n", + "Cbc0010I After 43200 nodes, 13934 on tree, 140987.35 best solution, best possible 137522.91 (766.06 seconds)\n", + "Cbc0010I After 43300 nodes, 13932 on tree, 140987.35 best solution, best possible 137522.91 (767.06 seconds)\n", + "Cbc0010I After 43400 nodes, 13924 on tree, 140987.35 best solution, best possible 137522.91 (767.69 seconds)\n", + "Cbc0010I After 43500 nodes, 13925 on tree, 140987.35 best solution, best possible 137522.91 (768.62 seconds)\n", + "Cbc0010I After 43600 nodes, 13923 on tree, 140987.35 best solution, best possible 137522.91 (769.58 seconds)\n", + "Cbc0010I After 43700 nodes, 13902 on tree, 140987.35 best solution, best possible 137522.91 (770.35 seconds)\n", + "Cbc0010I After 43800 nodes, 13903 on tree, 140987.35 best solution, best possible 137522.91 (771.24 seconds)\n", + "Cbc0010I After 43900 nodes, 13896 on tree, 140987.35 best solution, best possible 137522.91 (771.90 seconds)\n", + "Cbc0010I After 44000 nodes, 13900 on tree, 140987.35 best solution, best possible 137522.91 (772.69 seconds)\n", + "Cbc0010I After 44100 nodes, 13895 on tree, 140987.35 best solution, best possible 137522.91 (773.81 seconds)\n", + "Cbc0010I After 44200 nodes, 13893 on tree, 140987.35 best solution, best possible 137522.91 (774.59 seconds)\n", + "Cbc0010I After 44300 nodes, 13898 on tree, 140987.35 best solution, best possible 137522.91 (775.58 seconds)\n", + "Cbc0010I After 44400 nodes, 13895 on tree, 140987.35 best solution, best possible 137522.91 (776.75 seconds)\n", + "Cbc0010I After 44500 nodes, 13900 on tree, 140987.35 best solution, best possible 137522.91 (778.42 seconds)\n", + "Cbc0010I After 44600 nodes, 13908 on tree, 140987.35 best solution, best possible 137522.91 (779.26 seconds)\n", + "Cbc0010I After 44700 nodes, 13896 on tree, 140987.35 best solution, best possible 137522.91 (779.99 seconds)\n", + "Cbc0010I After 44800 nodes, 13891 on tree, 140987.35 best solution, best possible 137522.91 (780.90 seconds)\n", + "Cbc0010I After 44900 nodes, 13882 on tree, 140987.35 best solution, best possible 137522.91 (781.69 seconds)\n", + "Cbc0010I After 45000 nodes, 13856 on tree, 140987.35 best solution, best possible 137522.91 (782.49 seconds)\n", + "Cbc0010I After 45100 nodes, 13906 on tree, 140987.35 best solution, best possible 137524.36 (785.03 seconds)\n", + "Cbc0010I After 45200 nodes, 13956 on tree, 140987.35 best solution, best possible 137525.63 (786.84 seconds)\n", + "Cbc0010I After 45300 nodes, 14005 on tree, 140987.35 best solution, best possible 137527.03 (789.70 seconds)\n", + "Cbc0010I After 45400 nodes, 14055 on tree, 140987.35 best solution, best possible 137528.64 (792.83 seconds)\n", + "Cbc0010I After 45500 nodes, 14105 on tree, 140987.35 best solution, best possible 137530.11 (795.07 seconds)\n", + "Cbc0010I After 45600 nodes, 14154 on tree, 140987.35 best solution, best possible 137531.61 (797.02 seconds)\n", + "Cbc0010I After 45700 nodes, 14204 on tree, 140987.35 best solution, best possible 137533.72 (799.03 seconds)\n", + "Cbc0010I After 45800 nodes, 14254 on tree, 140987.35 best solution, best possible 137534.73 (801.04 seconds)\n", + "Cbc0010I After 45900 nodes, 14303 on tree, 140987.35 best solution, best possible 137536.34 (804.36 seconds)\n", + "Cbc0010I After 46000 nodes, 14353 on tree, 140987.35 best solution, best possible 137537.98 (806.67 seconds)\n", + "Cbc0010I After 46100 nodes, 14348 on tree, 140987.35 best solution, best possible 137537.98 (807.35 seconds)\n", + "Cbc0010I After 46200 nodes, 14344 on tree, 140987.35 best solution, best possible 137537.98 (808.10 seconds)\n", + "Cbc0010I After 46300 nodes, 14331 on tree, 140987.35 best solution, best possible 137537.98 (808.90 seconds)\n", + "Cbc0010I After 46400 nodes, 14326 on tree, 140987.35 best solution, best possible 137537.98 (809.68 seconds)\n", + "Cbc0010I After 46500 nodes, 14329 on tree, 140987.35 best solution, best possible 137537.98 (811.61 seconds)\n", + "Cbc0010I After 46600 nodes, 14330 on tree, 140987.35 best solution, best possible 137537.98 (812.64 seconds)\n", + "Cbc0010I After 46700 nodes, 14326 on tree, 140987.35 best solution, best possible 137537.98 (813.11 seconds)\n", + "Cbc0010I After 46800 nodes, 14333 on tree, 140987.35 best solution, best possible 137537.98 (814.26 seconds)\n", + "Cbc0010I After 46900 nodes, 14333 on tree, 140987.35 best solution, best possible 137537.98 (815.62 seconds)\n", + "Cbc0010I After 47000 nodes, 14331 on tree, 140987.35 best solution, best possible 137537.98 (817.38 seconds)\n", + "Cbc0010I After 47100 nodes, 14329 on tree, 140987.35 best solution, best possible 137537.98 (818.24 seconds)\n", + "Cbc0010I After 47200 nodes, 14333 on tree, 140987.35 best solution, best possible 137537.98 (818.95 seconds)\n", + "Cbc0010I After 47300 nodes, 14339 on tree, 140987.35 best solution, best possible 137537.98 (819.69 seconds)\n", + "Cbc0010I After 47400 nodes, 14331 on tree, 140987.35 best solution, best possible 137537.98 (820.59 seconds)\n", + "Cbc0010I After 47500 nodes, 14331 on tree, 140987.35 best solution, best possible 137537.98 (821.68 seconds)\n", + "Cbc0010I After 47600 nodes, 14332 on tree, 140987.35 best solution, best possible 137537.98 (822.65 seconds)\n", + "Cbc0010I After 47700 nodes, 14330 on tree, 140987.35 best solution, best possible 137537.98 (823.98 seconds)\n", + "Cbc0010I After 47800 nodes, 14334 on tree, 140987.35 best solution, best possible 137537.98 (825.10 seconds)\n", + "Cbc0010I After 47900 nodes, 14330 on tree, 140987.35 best solution, best possible 137537.98 (826.13 seconds)\n", + "Cbc0010I After 48000 nodes, 14328 on tree, 140987.35 best solution, best possible 137537.98 (826.88 seconds)\n", + "Cbc0010I After 48100 nodes, 14336 on tree, 140987.35 best solution, best possible 137537.98 (828.23 seconds)\n", + "Cbc0010I After 48200 nodes, 14328 on tree, 140987.35 best solution, best possible 137537.98 (829.57 seconds)\n", + "Cbc0012I Integer solution of 140830.11 found by heuristic after 3392091 iterations and 48280 nodes (830.35 seconds)\n", + "Cbc0010I After 48300 nodes, 14176 on tree, 140830.11 best solution, best possible 137537.98 (830.86 seconds)\n", + "Cbc0010I After 48400 nodes, 14225 on tree, 140830.11 best solution, best possible 137537.98 (832.94 seconds)\n", + "Cbc0010I After 48500 nodes, 14275 on tree, 140830.11 best solution, best possible 137537.98 (834.84 seconds)\n", + "Cbc0010I After 48600 nodes, 14323 on tree, 140830.11 best solution, best possible 137537.98 (836.52 seconds)\n", + "Cbc0010I After 48700 nodes, 14372 on tree, 140830.11 best solution, best possible 137537.98 (838.06 seconds)\n", + "Cbc0010I After 48800 nodes, 14417 on tree, 140830.11 best solution, best possible 137537.98 (839.63 seconds)\n", + "Cbc0010I After 48900 nodes, 14465 on tree, 140830.11 best solution, best possible 137537.98 (842.23 seconds)\n", + "Cbc0010I After 49000 nodes, 14514 on tree, 140830.11 best solution, best possible 137537.98 (844.12 seconds)\n", + "Cbc0012I Integer solution of 140824.46 found by heuristic after 3444338 iterations and 49021 nodes (844.25 seconds)\n", + "Cbc0010I After 49100 nodes, 14545 on tree, 140824.46 best solution, best possible 137537.98 (845.58 seconds)\n", + "Cbc0010I After 49200 nodes, 14594 on tree, 140824.46 best solution, best possible 137537.98 (847.64 seconds)\n", + "Cbc0010I After 49300 nodes, 14644 on tree, 140824.46 best solution, best possible 137537.98 (849.46 seconds)\n", + "Cbc0010I After 49400 nodes, 14693 on tree, 140824.46 best solution, best possible 137537.98 (851.38 seconds)\n", + "Cbc0010I After 49500 nodes, 14744 on tree, 140824.46 best solution, best possible 137537.98 (853.26 seconds)\n", + "Cbc0010I After 49600 nodes, 14793 on tree, 140824.46 best solution, best possible 137537.98 (856.21 seconds)\n", + "Cbc0010I After 49700 nodes, 14843 on tree, 140824.46 best solution, best possible 137537.98 (857.80 seconds)\n", + "Cbc0010I After 49800 nodes, 14891 on tree, 140824.46 best solution, best possible 137537.98 (859.82 seconds)\n", + "Cbc0010I After 49900 nodes, 14941 on tree, 140824.46 best solution, best possible 137537.98 (861.66 seconds)\n", + "Cbc0010I After 50000 nodes, 14989 on tree, 140824.46 best solution, best possible 137537.98 (863.56 seconds)\n", + "Cbc0010I After 50100 nodes, 14994 on tree, 140824.46 best solution, best possible 137537.98 (864.23 seconds)\n", + "Cbc0010I After 50200 nodes, 14986 on tree, 140824.46 best solution, best possible 137537.98 (864.91 seconds)\n", + "Cbc0010I After 50300 nodes, 14989 on tree, 140824.46 best solution, best possible 137537.98 (865.62 seconds)\n", + "Cbc0010I After 50400 nodes, 14993 on tree, 140824.46 best solution, best possible 137537.98 (866.93 seconds)\n", + "Cbc0010I After 50500 nodes, 14987 on tree, 140824.46 best solution, best possible 137537.98 (868.55 seconds)\n", + "Cbc0010I After 50600 nodes, 14963 on tree, 140824.46 best solution, best possible 137537.98 (869.30 seconds)\n", + "Cbc0010I After 50700 nodes, 14936 on tree, 140824.46 best solution, best possible 137537.98 (870.12 seconds)\n", + "Cbc0010I After 50800 nodes, 14931 on tree, 140824.46 best solution, best possible 137537.98 (870.84 seconds)\n", + "Cbc0010I After 50900 nodes, 14909 on tree, 140824.46 best solution, best possible 137537.98 (871.63 seconds)\n", + "Cbc0010I After 51000 nodes, 14920 on tree, 140824.46 best solution, best possible 137537.98 (872.86 seconds)\n", + "Cbc0010I After 51100 nodes, 14915 on tree, 140824.46 best solution, best possible 137537.98 (873.88 seconds)\n", + "Cbc0010I After 51200 nodes, 14913 on tree, 140824.46 best solution, best possible 137537.98 (874.90 seconds)\n", + "Cbc0010I After 51300 nodes, 14922 on tree, 140824.46 best solution, best possible 137537.98 (875.91 seconds)\n", + "Cbc0010I After 51400 nodes, 14913 on tree, 140824.46 best solution, best possible 137537.98 (877.12 seconds)\n", + "Cbc0010I After 51500 nodes, 14909 on tree, 140824.46 best solution, best possible 137537.98 (878.07 seconds)\n", + "Cbc0010I After 51600 nodes, 14900 on tree, 140824.46 best solution, best possible 137537.98 (879.06 seconds)\n", + "Cbc0010I After 51700 nodes, 14899 on tree, 140824.46 best solution, best possible 137537.98 (880.58 seconds)\n", + "Cbc0010I After 51800 nodes, 14898 on tree, 140824.46 best solution, best possible 137537.98 (881.78 seconds)\n", + "Cbc0010I After 51900 nodes, 14897 on tree, 140824.46 best solution, best possible 137537.98 (882.52 seconds)\n", + "Cbc0010I After 52000 nodes, 14898 on tree, 140824.46 best solution, best possible 137537.98 (883.83 seconds)\n", + "Cbc0010I After 52100 nodes, 14901 on tree, 140824.46 best solution, best possible 137537.98 (885.10 seconds)\n", + "Cbc0010I After 52200 nodes, 14897 on tree, 140824.46 best solution, best possible 137537.98 (885.95 seconds)\n", + "Cbc0010I After 52300 nodes, 14894 on tree, 140824.46 best solution, best possible 137537.98 (886.97 seconds)\n", + "Cbc0010I After 52400 nodes, 14896 on tree, 140824.46 best solution, best possible 137537.98 (888.21 seconds)\n", + "Cbc0010I After 52500 nodes, 14854 on tree, 140824.46 best solution, best possible 137537.98 (889.11 seconds)\n", + "Cbc0010I After 52600 nodes, 14828 on tree, 140824.46 best solution, best possible 137537.98 (890.30 seconds)\n", + "Cbc0010I After 52700 nodes, 14826 on tree, 140824.46 best solution, best possible 137537.98 (891.30 seconds)\n", + "Cbc0010I After 52800 nodes, 14827 on tree, 140824.46 best solution, best possible 137537.98 (892.58 seconds)\n", + "Cbc0010I After 52900 nodes, 14825 on tree, 140824.46 best solution, best possible 137537.98 (893.96 seconds)\n", + "Cbc0010I After 53000 nodes, 14832 on tree, 140824.46 best solution, best possible 137537.98 (895.17 seconds)\n", + "Cbc0010I After 53100 nodes, 14832 on tree, 140824.46 best solution, best possible 137537.98 (895.82 seconds)\n", + "Cbc0010I After 53200 nodes, 14828 on tree, 140824.46 best solution, best possible 137537.98 (896.44 seconds)\n", + "Cbc0010I After 53300 nodes, 14825 on tree, 140824.46 best solution, best possible 137537.98 (897.86 seconds)\n", + "Cbc0010I After 53400 nodes, 14836 on tree, 140824.46 best solution, best possible 137537.98 (898.84 seconds)\n", + "Cbc0010I After 53500 nodes, 14829 on tree, 140824.46 best solution, best possible 137537.98 (899.71 seconds)\n", + "Cbc0010I After 53600 nodes, 14827 on tree, 140824.46 best solution, best possible 137537.98 (900.59 seconds)\n", + "Cbc0010I After 53700 nodes, 14831 on tree, 140824.46 best solution, best possible 137537.98 (901.68 seconds)\n", + "Cbc0010I After 53800 nodes, 14837 on tree, 140824.46 best solution, best possible 137537.98 (902.77 seconds)\n", + "Cbc0010I After 53900 nodes, 14833 on tree, 140824.46 best solution, best possible 137537.98 (903.60 seconds)\n", + "Cbc0010I After 54000 nodes, 14836 on tree, 140824.46 best solution, best possible 137537.98 (904.89 seconds)\n", + "Cbc0010I After 54100 nodes, 14826 on tree, 140824.46 best solution, best possible 137537.98 (906.77 seconds)\n", + "Cbc0010I After 54200 nodes, 14825 on tree, 140824.46 best solution, best possible 137537.98 (908.13 seconds)\n", + "Cbc0010I After 54300 nodes, 14825 on tree, 140824.46 best solution, best possible 137537.98 (909.18 seconds)\n", + "Cbc0010I After 54400 nodes, 14830 on tree, 140824.46 best solution, best possible 137537.98 (910.13 seconds)\n", + "Cbc0010I After 54500 nodes, 14833 on tree, 140824.46 best solution, best possible 137537.98 (910.88 seconds)\n", + "Cbc0010I After 54600 nodes, 14829 on tree, 140824.46 best solution, best possible 137537.98 (911.47 seconds)\n", + "Cbc0010I After 54700 nodes, 14837 on tree, 140824.46 best solution, best possible 137537.98 (912.28 seconds)\n", + "Cbc0010I After 54800 nodes, 14829 on tree, 140824.46 best solution, best possible 137537.98 (913.06 seconds)\n", + "Cbc0010I After 54900 nodes, 14837 on tree, 140824.46 best solution, best possible 137537.98 (914.05 seconds)\n", + "Cbc0010I After 55000 nodes, 14830 on tree, 140824.46 best solution, best possible 137537.98 (915.10 seconds)\n", + "Cbc0010I After 55100 nodes, 14827 on tree, 140824.46 best solution, best possible 137537.98 (915.96 seconds)\n", + "Cbc0010I After 55200 nodes, 14835 on tree, 140824.46 best solution, best possible 137537.98 (916.88 seconds)\n", + "Cbc0010I After 55300 nodes, 14834 on tree, 140824.46 best solution, best possible 137537.98 (917.87 seconds)\n", + "Cbc0010I After 55400 nodes, 14829 on tree, 140824.46 best solution, best possible 137537.98 (919.36 seconds)\n", + "Cbc0010I After 55500 nodes, 14802 on tree, 140824.46 best solution, best possible 137537.98 (920.52 seconds)\n", + "Cbc0010I After 55600 nodes, 14778 on tree, 140824.46 best solution, best possible 137537.98 (921.49 seconds)\n", + "Cbc0010I After 55700 nodes, 14752 on tree, 140824.46 best solution, best possible 137537.98 (922.35 seconds)\n", + "Cbc0010I After 55800 nodes, 14746 on tree, 140824.46 best solution, best possible 137537.98 (923.24 seconds)\n", + "Cbc0010I After 55900 nodes, 14750 on tree, 140824.46 best solution, best possible 137537.98 (924.22 seconds)\n", + "Cbc0010I After 56000 nodes, 14735 on tree, 140824.46 best solution, best possible 137537.98 (925.05 seconds)\n", + "Cbc0010I After 56100 nodes, 14734 on tree, 140824.46 best solution, best possible 137537.98 (925.88 seconds)\n", + "Cbc0010I After 56200 nodes, 14736 on tree, 140824.46 best solution, best possible 137537.98 (927.11 seconds)\n", + "Cbc0010I After 56300 nodes, 14733 on tree, 140824.46 best solution, best possible 137537.98 (928.16 seconds)\n", + "Cbc0010I After 56400 nodes, 14738 on tree, 140824.46 best solution, best possible 137537.98 (929.11 seconds)\n", + "Cbc0010I After 56500 nodes, 14743 on tree, 140824.46 best solution, best possible 137537.98 (930.18 seconds)\n", + "Cbc0010I After 56600 nodes, 14737 on tree, 140824.46 best solution, best possible 137537.98 (932.11 seconds)\n", + "Cbc0010I After 56700 nodes, 14741 on tree, 140824.46 best solution, best possible 137537.98 (933.31 seconds)\n", + "Cbc0010I After 56800 nodes, 14735 on tree, 140824.46 best solution, best possible 137537.98 (934.36 seconds)\n", + "Cbc0010I After 56900 nodes, 14747 on tree, 140824.46 best solution, best possible 137537.98 (935.55 seconds)\n", + "Cbc0010I After 57000 nodes, 14746 on tree, 140824.46 best solution, best possible 137537.98 (936.74 seconds)\n", + "Cbc0010I After 57100 nodes, 14740 on tree, 140824.46 best solution, best possible 137537.98 (937.78 seconds)\n", + "Cbc0010I After 57200 nodes, 14739 on tree, 140824.46 best solution, best possible 137537.98 (938.53 seconds)\n", + "Cbc0010I After 57300 nodes, 14739 on tree, 140824.46 best solution, best possible 137537.98 (939.26 seconds)\n", + "Cbc0010I After 57400 nodes, 14740 on tree, 140824.46 best solution, best possible 137537.98 (939.84 seconds)\n", + "Cbc0010I After 57500 nodes, 14738 on tree, 140824.46 best solution, best possible 137537.98 (940.42 seconds)\n", + "Cbc0010I After 57600 nodes, 14742 on tree, 140824.46 best solution, best possible 137537.98 (941.33 seconds)\n", + "Cbc0012I Integer solution of 140543.97 found by heuristic after 3867166 iterations and 57647 nodes (941.77 seconds)\n", + "Cbc0010I After 57700 nodes, 14459 on tree, 140543.97 best solution, best possible 137537.98 (943.76 seconds)\n", + "Cbc0010I After 57800 nodes, 14509 on tree, 140543.97 best solution, best possible 137537.98 (946.74 seconds)\n", + "Cbc0010I After 57900 nodes, 14560 on tree, 140543.97 best solution, best possible 137537.98 (949.01 seconds)\n", + "Cbc0010I After 58000 nodes, 14610 on tree, 140543.97 best solution, best possible 137537.98 (951.33 seconds)\n", + "Cbc0010I After 58100 nodes, 14606 on tree, 140543.97 best solution, best possible 137537.98 (952.25 seconds)\n", + "Cbc0010I After 58200 nodes, 14614 on tree, 140543.97 best solution, best possible 137537.98 (953.03 seconds)\n", + "Cbc0010I After 58300 nodes, 14614 on tree, 140543.97 best solution, best possible 137537.98 (953.87 seconds)\n", + "Cbc0010I After 58400 nodes, 14615 on tree, 140543.97 best solution, best possible 137537.98 (954.69 seconds)\n", + "Cbc0010I After 58500 nodes, 14612 on tree, 140543.97 best solution, best possible 137537.98 (955.61 seconds)\n", + "Cbc0010I After 58600 nodes, 14607 on tree, 140543.97 best solution, best possible 137537.98 (956.86 seconds)\n", + "Cbc0010I After 58700 nodes, 14580 on tree, 140543.97 best solution, best possible 137537.98 (958.62 seconds)\n", + "Cbc0010I After 58800 nodes, 14573 on tree, 140543.97 best solution, best possible 137537.98 (959.46 seconds)\n", + "Cbc0010I After 58900 nodes, 14554 on tree, 140543.97 best solution, best possible 137537.98 (960.43 seconds)\n", + "Cbc0010I After 59000 nodes, 14560 on tree, 140543.97 best solution, best possible 137537.98 (961.24 seconds)\n", + "Cbc0010I After 59100 nodes, 14556 on tree, 140543.97 best solution, best possible 137537.98 (961.98 seconds)\n", + "Cbc0010I After 59200 nodes, 14562 on tree, 140543.97 best solution, best possible 137537.98 (963.10 seconds)\n", + "Cbc0010I After 59300 nodes, 14559 on tree, 140543.97 best solution, best possible 137537.98 (964.09 seconds)\n", + "Cbc0010I After 59400 nodes, 14566 on tree, 140543.97 best solution, best possible 137537.98 (964.86 seconds)\n", + "Cbc0010I After 59500 nodes, 14562 on tree, 140543.97 best solution, best possible 137537.98 (966.01 seconds)\n", + "Cbc0010I After 59600 nodes, 14559 on tree, 140543.97 best solution, best possible 137537.98 (967.02 seconds)\n", + "Cbc0010I After 59700 nodes, 14564 on tree, 140543.97 best solution, best possible 137537.98 (968.05 seconds)\n", + "Cbc0010I After 59800 nodes, 14565 on tree, 140543.97 best solution, best possible 137537.98 (969.05 seconds)\n", + "Cbc0010I After 59900 nodes, 14564 on tree, 140543.97 best solution, best possible 137537.98 (970.99 seconds)\n", + "Cbc0010I After 60000 nodes, 14565 on tree, 140543.97 best solution, best possible 137537.98 (972.14 seconds)\n", + "Cbc0010I After 60100 nodes, 14556 on tree, 140543.97 best solution, best possible 137537.98 (973.10 seconds)\n", + "Cbc0012I Integer solution of 140529.66 found by heuristic after 3993788 iterations and 60145 nodes (973.39 seconds)\n", + "Cbc0010I After 60200 nodes, 14588 on tree, 140529.66 best solution, best possible 137537.98 (975.05 seconds)\n", + "Cbc0010I After 60300 nodes, 14638 on tree, 140529.66 best solution, best possible 137537.98 (977.22 seconds)\n", + "Cbc0010I After 60400 nodes, 14688 on tree, 140529.66 best solution, best possible 137537.98 (979.23 seconds)\n", + "Cbc0010I After 60500 nodes, 14738 on tree, 140529.66 best solution, best possible 137537.98 (981.44 seconds)\n", + "Cbc0010I After 60600 nodes, 14789 on tree, 140529.66 best solution, best possible 137537.98 (984.92 seconds)\n", + "Cbc0010I After 60700 nodes, 14838 on tree, 140529.66 best solution, best possible 137537.98 (987.10 seconds)\n", + "Cbc0010I After 60800 nodes, 14887 on tree, 140529.66 best solution, best possible 137537.98 (989.07 seconds)\n", + "Cbc0010I After 60900 nodes, 14936 on tree, 140529.66 best solution, best possible 137537.98 (991.12 seconds)\n", + "Cbc0010I After 61000 nodes, 14986 on tree, 140529.66 best solution, best possible 137537.98 (993.19 seconds)\n", + "Cbc0010I After 61100 nodes, 14984 on tree, 140529.66 best solution, best possible 137537.98 (993.66 seconds)\n", + "Cbc0010I After 61200 nodes, 14977 on tree, 140529.66 best solution, best possible 137537.98 (994.35 seconds)\n", + "Cbc0012I Integer solution of 140524.01 found by heuristic after 4074485 iterations and 61209 nodes (994.42 seconds)\n", + "Cbc0010I After 61300 nodes, 15015 on tree, 140524.01 best solution, best possible 137537.98 (997.15 seconds)\n", + "Cbc0010I After 61400 nodes, 15064 on tree, 140524.01 best solution, best possible 137537.98 (999.63 seconds)\n", + "Cbc0010I After 61500 nodes, 15114 on tree, 140524.01 best solution, best possible 137537.98 (1001.80 seconds)\n", + "Cbc0010I After 61600 nodes, 15161 on tree, 140524.01 best solution, best possible 137537.98 (1003.74 seconds)\n", + "Cbc0010I After 61700 nodes, 15211 on tree, 140524.01 best solution, best possible 137537.98 (1005.60 seconds)\n", + "Cbc0010I After 61800 nodes, 15259 on tree, 140524.01 best solution, best possible 137537.98 (1007.86 seconds)\n", + "Cbc0010I After 61900 nodes, 15309 on tree, 140524.01 best solution, best possible 137537.98 (1011.21 seconds)\n", + "Cbc0010I After 62000 nodes, 15356 on tree, 140524.01 best solution, best possible 137537.98 (1012.89 seconds)\n", + "Cbc0010I After 62100 nodes, 15349 on tree, 140524.01 best solution, best possible 137537.98 (1013.50 seconds)\n", + "Cbc0010I After 62200 nodes, 15351 on tree, 140524.01 best solution, best possible 137537.98 (1014.99 seconds)\n", + "Cbc0010I After 62300 nodes, 15352 on tree, 140524.01 best solution, best possible 137537.98 (1015.86 seconds)\n", + "Cbc0012I Integer solution of 140249.17 found by heuristic after 4157609 iterations and 62374 nodes (1016.39 seconds)\n", + "Cbc0010I After 62400 nodes, 14972 on tree, 140249.17 best solution, best possible 137537.98 (1016.86 seconds)\n", + "Cbc0010I After 62500 nodes, 15022 on tree, 140249.17 best solution, best possible 137537.98 (1018.81 seconds)\n", + "Cbc0010I After 62600 nodes, 15072 on tree, 140249.17 best solution, best possible 137537.98 (1020.97 seconds)\n", + "Cbc0010I After 62700 nodes, 15123 on tree, 140249.17 best solution, best possible 137537.98 (1024.08 seconds)\n", + "Cbc0010I After 62800 nodes, 15171 on tree, 140249.17 best solution, best possible 137537.98 (1025.98 seconds)\n", + "Cbc0010I After 62900 nodes, 15219 on tree, 140249.17 best solution, best possible 137537.98 (1028.04 seconds)\n", + "Cbc0010I After 63000 nodes, 15270 on tree, 140249.17 best solution, best possible 137537.98 (1030.00 seconds)\n", + "Cbc0012I Integer solution of 140249.17 found by heuristic after 4209775 iterations and 63014 nodes (1030.19 seconds)\n", + "Cbc0010I After 63100 nodes, 15308 on tree, 140249.17 best solution, best possible 137537.98 (1031.81 seconds)\n", + "Cbc0010I After 63200 nodes, 15356 on tree, 140249.17 best solution, best possible 137537.98 (1033.83 seconds)\n", + "Cbc0010I After 63300 nodes, 15405 on tree, 140249.17 best solution, best possible 137537.98 (1036.82 seconds)\n", + "Cbc0010I After 63400 nodes, 15453 on tree, 140249.17 best solution, best possible 137537.98 (1038.67 seconds)\n", + "Cbc0010I After 63500 nodes, 15502 on tree, 140249.17 best solution, best possible 137537.98 (1040.39 seconds)\n", + "Cbc0010I After 63600 nodes, 15551 on tree, 140249.17 best solution, best possible 137537.98 (1042.32 seconds)\n", + "Cbc0010I After 63700 nodes, 15600 on tree, 140249.17 best solution, best possible 137537.98 (1044.22 seconds)\n", + "Cbc0010I After 63800 nodes, 15649 on tree, 140249.17 best solution, best possible 137537.98 (1046.47 seconds)\n", + "Cbc0010I After 63900 nodes, 15700 on tree, 140249.17 best solution, best possible 137537.98 (1049.39 seconds)\n", + "Cbc0010I After 64000 nodes, 15748 on tree, 140249.17 best solution, best possible 137537.98 (1051.14 seconds)\n", + "Cbc0010I After 64100 nodes, 15754 on tree, 140249.17 best solution, best possible 137537.98 (1052.03 seconds)\n", + "Cbc0010I After 64200 nodes, 15747 on tree, 140249.17 best solution, best possible 137537.98 (1052.54 seconds)\n", + "Cbc0010I After 64300 nodes, 15713 on tree, 140249.17 best solution, best possible 137537.98 (1053.19 seconds)\n", + "Cbc0010I After 64400 nodes, 15695 on tree, 140249.17 best solution, best possible 137537.98 (1054.21 seconds)\n", + "Cbc0010I After 64500 nodes, 15680 on tree, 140249.17 best solution, best possible 137537.98 (1055.31 seconds)\n", + "Cbc0010I After 64600 nodes, 15680 on tree, 140249.17 best solution, best possible 137537.98 (1056.38 seconds)\n", + "Cbc0010I After 64700 nodes, 15680 on tree, 140249.17 best solution, best possible 137537.98 (1057.48 seconds)\n", + "Cbc0010I After 64800 nodes, 15679 on tree, 140249.17 best solution, best possible 137537.98 (1058.32 seconds)\n", + "Cbc0010I After 64900 nodes, 15686 on tree, 140249.17 best solution, best possible 137537.98 (1059.09 seconds)\n", + "Cbc0010I After 65000 nodes, 15683 on tree, 140249.17 best solution, best possible 137537.98 (1060.97 seconds)\n", + "Cbc0010I After 65100 nodes, 15682 on tree, 140249.17 best solution, best possible 137537.98 (1062.57 seconds)\n", + "Cbc0010I After 65200 nodes, 15648 on tree, 140249.17 best solution, best possible 137537.98 (1063.50 seconds)\n", + "Cbc0010I After 65300 nodes, 15623 on tree, 140249.17 best solution, best possible 137537.98 (1064.61 seconds)\n", + "Cbc0010I After 65400 nodes, 15626 on tree, 140249.17 best solution, best possible 137537.98 (1065.87 seconds)\n", + "Cbc0010I After 65500 nodes, 15627 on tree, 140249.17 best solution, best possible 137537.98 (1066.68 seconds)\n", + "Cbc0010I After 65600 nodes, 15625 on tree, 140249.17 best solution, best possible 137537.98 (1067.77 seconds)\n", + "Cbc0010I After 65700 nodes, 15625 on tree, 140249.17 best solution, best possible 137537.98 (1069.04 seconds)\n", + "Cbc0010I After 65800 nodes, 15613 on tree, 140249.17 best solution, best possible 137537.98 (1069.86 seconds)\n", + "Cbc0010I After 65900 nodes, 15603 on tree, 140249.17 best solution, best possible 137537.98 (1071.01 seconds)\n", + "Cbc0010I After 66000 nodes, 15597 on tree, 140249.17 best solution, best possible 137537.98 (1072.32 seconds)\n", + "Cbc0010I After 66100 nodes, 15585 on tree, 140249.17 best solution, best possible 137537.98 (1074.70 seconds)\n", + "Cbc0010I After 66200 nodes, 15587 on tree, 140249.17 best solution, best possible 137537.98 (1076.03 seconds)\n", + "Cbc0010I After 66300 nodes, 15594 on tree, 140249.17 best solution, best possible 137537.98 (1077.07 seconds)\n", + "Cbc0010I After 66400 nodes, 15588 on tree, 140249.17 best solution, best possible 137537.98 (1078.32 seconds)\n", + "Cbc0010I After 66500 nodes, 15589 on tree, 140249.17 best solution, best possible 137537.98 (1079.37 seconds)\n", + "Cbc0010I After 66600 nodes, 15586 on tree, 140249.17 best solution, best possible 137537.98 (1080.74 seconds)\n", + "Cbc0010I After 66700 nodes, 15588 on tree, 140249.17 best solution, best possible 137537.98 (1081.85 seconds)\n", + "Cbc0010I After 66800 nodes, 15595 on tree, 140249.17 best solution, best possible 137537.98 (1083.25 seconds)\n", + "Cbc0010I After 66900 nodes, 15586 on tree, 140249.17 best solution, best possible 137537.98 (1084.31 seconds)\n", + "Cbc0010I After 67000 nodes, 15596 on tree, 140249.17 best solution, best possible 137537.98 (1085.57 seconds)\n", + "Cbc0010I After 67100 nodes, 15591 on tree, 140249.17 best solution, best possible 137537.98 (1086.71 seconds)\n", + "Cbc0010I After 67200 nodes, 15597 on tree, 140249.17 best solution, best possible 137537.98 (1087.97 seconds)\n", + "Cbc0010I After 67300 nodes, 15593 on tree, 140249.17 best solution, best possible 137537.98 (1088.68 seconds)\n", + "Cbc0010I After 67400 nodes, 15589 on tree, 140249.17 best solution, best possible 137537.98 (1089.58 seconds)\n", + "Cbc0010I After 67500 nodes, 15585 on tree, 140249.17 best solution, best possible 137537.98 (1090.66 seconds)\n", + "Cbc0010I After 67600 nodes, 15565 on tree, 140249.17 best solution, best possible 137537.98 (1091.67 seconds)\n", + "Cbc0010I After 67700 nodes, 15555 on tree, 140249.17 best solution, best possible 137537.98 (1092.88 seconds)\n", + "Cbc0010I After 67800 nodes, 15535 on tree, 140249.17 best solution, best possible 137537.98 (1093.97 seconds)\n", + "Cbc0010I After 67900 nodes, 15531 on tree, 140249.17 best solution, best possible 137537.98 (1095.11 seconds)\n", + "Cbc0010I After 68000 nodes, 15536 on tree, 140249.17 best solution, best possible 137537.98 (1096.13 seconds)\n", + "Cbc0010I After 68100 nodes, 15545 on tree, 140249.17 best solution, best possible 137537.98 (1096.86 seconds)\n", + "Cbc0010I After 68200 nodes, 15542 on tree, 140249.17 best solution, best possible 137537.98 (1097.44 seconds)\n", + "Cbc0010I After 68300 nodes, 15532 on tree, 140249.17 best solution, best possible 137537.98 (1099.23 seconds)\n", + "Cbc0010I After 68400 nodes, 15531 on tree, 140249.17 best solution, best possible 137537.98 (1100.88 seconds)\n", + "Cbc0010I After 68500 nodes, 15531 on tree, 140249.17 best solution, best possible 137537.98 (1101.85 seconds)\n", + "Cbc0010I After 68600 nodes, 15534 on tree, 140249.17 best solution, best possible 137537.98 (1103.04 seconds)\n", + "Cbc0010I After 68700 nodes, 15536 on tree, 140249.17 best solution, best possible 137537.98 (1104.04 seconds)\n", + "Cbc0010I After 68800 nodes, 15534 on tree, 140249.17 best solution, best possible 137537.98 (1104.88 seconds)\n", + "Cbc0010I After 68900 nodes, 15516 on tree, 140249.17 best solution, best possible 137537.98 (1105.71 seconds)\n", + "Cbc0010I After 69000 nodes, 15488 on tree, 140249.17 best solution, best possible 137537.98 (1106.69 seconds)\n", + "Cbc0010I After 69100 nodes, 15487 on tree, 140249.17 best solution, best possible 137537.98 (1107.66 seconds)\n", + "Cbc0010I After 69200 nodes, 15496 on tree, 140249.17 best solution, best possible 137537.98 (1108.80 seconds)\n", + "Cbc0010I After 69300 nodes, 15497 on tree, 140249.17 best solution, best possible 137537.98 (1110.00 seconds)\n", + "Cbc0010I After 69400 nodes, 15490 on tree, 140249.17 best solution, best possible 137537.98 (1111.72 seconds)\n", + "Cbc0010I After 69500 nodes, 15488 on tree, 140249.17 best solution, best possible 137537.98 (1114.01 seconds)\n", + "Cbc0010I After 69600 nodes, 15482 on tree, 140249.17 best solution, best possible 137537.98 (1114.78 seconds)\n", + "Cbc0010I After 69700 nodes, 15483 on tree, 140249.17 best solution, best possible 137537.98 (1115.94 seconds)\n", + "Cbc0010I After 69800 nodes, 15482 on tree, 140249.17 best solution, best possible 137537.98 (1117.03 seconds)\n", + "Cbc0010I After 69900 nodes, 15484 on tree, 140249.17 best solution, best possible 137537.98 (1117.99 seconds)\n", + "Cbc0010I After 70000 nodes, 15481 on tree, 140249.17 best solution, best possible 137537.98 (1118.90 seconds)\n", + "Cbc0010I After 70100 nodes, 15481 on tree, 140249.17 best solution, best possible 137537.98 (1119.85 seconds)\n", + "Cbc0010I After 70200 nodes, 15485 on tree, 140249.17 best solution, best possible 137537.98 (1121.32 seconds)\n", + "Cbc0010I After 70300 nodes, 15483 on tree, 140249.17 best solution, best possible 137537.98 (1122.60 seconds)\n", + "Cbc0010I After 70400 nodes, 15478 on tree, 140249.17 best solution, best possible 137537.98 (1123.44 seconds)\n", + "Cbc0010I After 70500 nodes, 15488 on tree, 140249.17 best solution, best possible 137537.98 (1124.87 seconds)\n", + "Cbc0010I After 70600 nodes, 15489 on tree, 140249.17 best solution, best possible 137537.98 (1125.93 seconds)\n", + "Cbc0010I After 70700 nodes, 15486 on tree, 140249.17 best solution, best possible 137537.98 (1126.83 seconds)\n", + "Cbc0010I After 70800 nodes, 15483 on tree, 140249.17 best solution, best possible 137537.98 (1127.53 seconds)\n", + "Cbc0010I After 70900 nodes, 15484 on tree, 140249.17 best solution, best possible 137537.98 (1128.72 seconds)\n", + "Cbc0010I After 71000 nodes, 15484 on tree, 140249.17 best solution, best possible 137537.98 (1129.98 seconds)\n", + "Cbc0010I After 71100 nodes, 15484 on tree, 140249.17 best solution, best possible 137537.98 (1131.11 seconds)\n", + "Cbc0010I After 71200 nodes, 15481 on tree, 140249.17 best solution, best possible 137537.98 (1132.31 seconds)\n", + "Cbc0010I After 71300 nodes, 15482 on tree, 140249.17 best solution, best possible 137537.98 (1133.40 seconds)\n", + "Cbc0010I After 71400 nodes, 15479 on tree, 140249.17 best solution, best possible 137537.98 (1134.60 seconds)\n", + "Cbc0010I After 71500 nodes, 15482 on tree, 140249.17 best solution, best possible 137537.98 (1135.60 seconds)\n", + "Cbc0010I After 71600 nodes, 15486 on tree, 140249.17 best solution, best possible 137537.98 (1136.80 seconds)\n", + "Cbc0010I After 71700 nodes, 15493 on tree, 140249.17 best solution, best possible 137537.98 (1138.17 seconds)\n", + "Cbc0010I After 71800 nodes, 15483 on tree, 140249.17 best solution, best possible 137537.98 (1139.57 seconds)\n", + "Cbc0010I After 71900 nodes, 15482 on tree, 140249.17 best solution, best possible 137537.98 (1140.67 seconds)\n", + "Cbc0010I After 72000 nodes, 15474 on tree, 140249.17 best solution, best possible 137537.98 (1141.59 seconds)\n", + "Cbc0010I After 72100 nodes, 15446 on tree, 140249.17 best solution, best possible 137537.98 (1142.67 seconds)\n", + "Cbc0010I After 72200 nodes, 15410 on tree, 140249.17 best solution, best possible 137537.98 (1143.65 seconds)\n", + "Cbc0010I After 72300 nodes, 15406 on tree, 140249.17 best solution, best possible 137537.98 (1144.84 seconds)\n", + "Cbc0010I After 72400 nodes, 15400 on tree, 140249.17 best solution, best possible 137537.98 (1146.00 seconds)\n", + "Cbc0010I After 72500 nodes, 15399 on tree, 140249.17 best solution, best possible 137537.98 (1146.78 seconds)\n", + "Cbc0010I After 72600 nodes, 15388 on tree, 140249.17 best solution, best possible 137537.98 (1147.86 seconds)\n", + "Cbc0010I After 72700 nodes, 15373 on tree, 140249.17 best solution, best possible 137537.98 (1148.99 seconds)\n", + "Cbc0010I After 72800 nodes, 15373 on tree, 140249.17 best solution, best possible 137537.98 (1150.59 seconds)\n", + "Cbc0010I After 72900 nodes, 15375 on tree, 140249.17 best solution, best possible 137537.98 (1152.59 seconds)\n", + "Cbc0010I After 73000 nodes, 15389 on tree, 140249.17 best solution, best possible 137537.98 (1153.63 seconds)\n", + "Cbc0010I After 73100 nodes, 15385 on tree, 140249.17 best solution, best possible 137537.98 (1154.48 seconds)\n", + "Cbc0010I After 73200 nodes, 15379 on tree, 140249.17 best solution, best possible 137537.98 (1155.46 seconds)\n", + "Cbc0010I After 73300 nodes, 15374 on tree, 140249.17 best solution, best possible 137537.98 (1156.59 seconds)\n", + "Cbc0010I After 73400 nodes, 15376 on tree, 140249.17 best solution, best possible 137537.98 (1157.57 seconds)\n", + "Cbc0010I After 73500 nodes, 15374 on tree, 140249.17 best solution, best possible 137537.98 (1158.43 seconds)\n", + "Cbc0010I After 73600 nodes, 15374 on tree, 140249.17 best solution, best possible 137537.98 (1159.54 seconds)\n", + "Cbc0010I After 73700 nodes, 15380 on tree, 140249.17 best solution, best possible 137537.98 (1161.00 seconds)\n", + "Cbc0010I After 73800 nodes, 15372 on tree, 140249.17 best solution, best possible 137537.98 (1162.60 seconds)\n", + "Cbc0010I After 73900 nodes, 15379 on tree, 140249.17 best solution, best possible 137537.98 (1164.54 seconds)\n", + "Cbc0010I After 74000 nodes, 15371 on tree, 140249.17 best solution, best possible 137537.98 (1165.88 seconds)\n", + "Cbc0010I After 74100 nodes, 15377 on tree, 140249.17 best solution, best possible 137537.98 (1166.87 seconds)\n", + "Cbc0010I After 74200 nodes, 15381 on tree, 140249.17 best solution, best possible 137537.98 (1167.83 seconds)\n", + "Cbc0010I After 74300 nodes, 15376 on tree, 140249.17 best solution, best possible 137537.98 (1168.57 seconds)\n", + "Cbc0010I After 74400 nodes, 15381 on tree, 140249.17 best solution, best possible 137537.98 (1169.41 seconds)\n", + "Cbc0010I After 74500 nodes, 15385 on tree, 140249.17 best solution, best possible 137537.98 (1170.20 seconds)\n", + "Cbc0010I After 74600 nodes, 15378 on tree, 140249.17 best solution, best possible 137537.98 (1171.12 seconds)\n", + "Cbc0010I After 74700 nodes, 15380 on tree, 140249.17 best solution, best possible 137537.98 (1172.41 seconds)\n", + "Cbc0010I After 74800 nodes, 15375 on tree, 140249.17 best solution, best possible 137537.98 (1173.59 seconds)\n", + "Cbc0010I After 74900 nodes, 15382 on tree, 140249.17 best solution, best possible 137537.98 (1174.30 seconds)\n", + "Cbc0010I After 75000 nodes, 15376 on tree, 140249.17 best solution, best possible 137537.98 (1175.32 seconds)\n", + "Cbc0010I After 75100 nodes, 15385 on tree, 140249.17 best solution, best possible 137537.98 (1177.91 seconds)\n", + "Cbc0010I After 75200 nodes, 15377 on tree, 140249.17 best solution, best possible 137537.98 (1178.86 seconds)\n", + "Cbc0010I After 75300 nodes, 15377 on tree, 140249.17 best solution, best possible 137537.98 (1180.03 seconds)\n", + "Cbc0010I After 75400 nodes, 15374 on tree, 140249.17 best solution, best possible 137537.98 (1181.09 seconds)\n", + "Cbc0010I After 75500 nodes, 15375 on tree, 140249.17 best solution, best possible 137537.98 (1181.84 seconds)\n", + "Cbc0010I After 75600 nodes, 15350 on tree, 140249.17 best solution, best possible 137537.98 (1182.85 seconds)\n", + "Cbc0010I After 75700 nodes, 15302 on tree, 140249.17 best solution, best possible 137537.98 (1184.27 seconds)\n", + "Cbc0010I After 75800 nodes, 15302 on tree, 140249.17 best solution, best possible 137537.98 (1185.52 seconds)\n", + "Cbc0010I After 75900 nodes, 15298 on tree, 140249.17 best solution, best possible 137537.98 (1186.63 seconds)\n", + "Cbc0010I After 76000 nodes, 15281 on tree, 140249.17 best solution, best possible 137537.98 (1187.39 seconds)\n", + "Cbc0010I After 76100 nodes, 15289 on tree, 140249.17 best solution, best possible 137537.98 (1188.10 seconds)\n", + "Cbc0010I After 76200 nodes, 15283 on tree, 140249.17 best solution, best possible 137537.98 (1189.84 seconds)\n", + "Cbc0010I After 76300 nodes, 15286 on tree, 140249.17 best solution, best possible 137537.98 (1191.01 seconds)\n", + "Cbc0010I After 76400 nodes, 15289 on tree, 140249.17 best solution, best possible 137537.98 (1191.99 seconds)\n", + "Cbc0010I After 76500 nodes, 15292 on tree, 140249.17 best solution, best possible 137537.98 (1193.17 seconds)\n", + "Cbc0010I After 76600 nodes, 15284 on tree, 140249.17 best solution, best possible 137537.98 (1194.33 seconds)\n", + "Cbc0010I After 76700 nodes, 15284 on tree, 140249.17 best solution, best possible 137537.98 (1195.72 seconds)\n", + "Cbc0010I After 76800 nodes, 15282 on tree, 140249.17 best solution, best possible 137537.98 (1197.02 seconds)\n", + "Cbc0010I After 76900 nodes, 15274 on tree, 140249.17 best solution, best possible 137537.98 (1197.83 seconds)\n", + "Cbc0010I After 77000 nodes, 15278 on tree, 140249.17 best solution, best possible 137537.98 (1198.63 seconds)\n", + "Cbc0010I After 77100 nodes, 15276 on tree, 140249.17 best solution, best possible 137537.98 (1199.39 seconds)\n", + "Cbc0010I After 77200 nodes, 15271 on tree, 140249.17 best solution, best possible 137537.98 (1200.40 seconds)\n", + "Cbc0010I After 77300 nodes, 15274 on tree, 140249.17 best solution, best possible 137537.98 (1201.74 seconds)\n", + "Cbc0010I After 77400 nodes, 15274 on tree, 140249.17 best solution, best possible 137537.98 (1203.71 seconds)\n", + "Cbc0010I After 77500 nodes, 15270 on tree, 140249.17 best solution, best possible 137537.98 (1204.59 seconds)\n", + "Cbc0010I After 77600 nodes, 15269 on tree, 140249.17 best solution, best possible 137537.98 (1205.53 seconds)\n", + "Cbc0010I After 77700 nodes, 15269 on tree, 140249.17 best solution, best possible 137537.98 (1206.61 seconds)\n", + "Cbc0010I After 77800 nodes, 15270 on tree, 140249.17 best solution, best possible 137537.98 (1207.97 seconds)\n", + "Cbc0010I After 77900 nodes, 15276 on tree, 140249.17 best solution, best possible 137537.98 (1209.33 seconds)\n", + "Cbc0010I After 78000 nodes, 15276 on tree, 140249.17 best solution, best possible 137537.98 (1209.89 seconds)\n", + "Cbc0010I After 78100 nodes, 15279 on tree, 140249.17 best solution, best possible 137537.98 (1210.75 seconds)\n", + "Cbc0010I After 78200 nodes, 15275 on tree, 140249.17 best solution, best possible 137537.98 (1211.52 seconds)\n", + "Cbc0010I After 78300 nodes, 15279 on tree, 140249.17 best solution, best possible 137537.98 (1212.32 seconds)\n", + "Cbc0010I After 78400 nodes, 15278 on tree, 140249.17 best solution, best possible 137537.98 (1213.05 seconds)\n", + "Cbc0010I After 78500 nodes, 15278 on tree, 140249.17 best solution, best possible 137537.98 (1213.63 seconds)\n", + "Cbc0010I After 78600 nodes, 15278 on tree, 140249.17 best solution, best possible 137537.98 (1214.97 seconds)\n", + "Cbc0010I After 78700 nodes, 15273 on tree, 140249.17 best solution, best possible 137537.98 (1216.18 seconds)\n", + "Cbc0010I After 78800 nodes, 15274 on tree, 140249.17 best solution, best possible 137537.98 (1217.02 seconds)\n", + "Cbc0010I After 78900 nodes, 15279 on tree, 140249.17 best solution, best possible 137537.98 (1218.02 seconds)\n", + "Cbc0010I After 79000 nodes, 15271 on tree, 140249.17 best solution, best possible 137537.98 (1218.92 seconds)\n", + "Cbc0010I After 79100 nodes, 15270 on tree, 140249.17 best solution, best possible 137537.98 (1219.75 seconds)\n", + "Cbc0010I After 79200 nodes, 15277 on tree, 140249.17 best solution, best possible 137537.98 (1221.00 seconds)\n", + "Cbc0010I After 79300 nodes, 15271 on tree, 140249.17 best solution, best possible 137537.98 (1222.38 seconds)\n", + "Cbc0010I After 79400 nodes, 15279 on tree, 140249.17 best solution, best possible 137537.98 (1223.99 seconds)\n", + "Cbc0010I After 79500 nodes, 15272 on tree, 140249.17 best solution, best possible 137537.98 (1225.45 seconds)\n", + "Cbc0010I After 79600 nodes, 15277 on tree, 140249.17 best solution, best possible 137537.98 (1227.02 seconds)\n", + "Cbc0010I After 79700 nodes, 15275 on tree, 140249.17 best solution, best possible 137537.98 (1229.42 seconds)\n", + "Cbc0010I After 79800 nodes, 15272 on tree, 140249.17 best solution, best possible 137537.98 (1230.85 seconds)\n", + "Cbc0010I After 79900 nodes, 15271 on tree, 140249.17 best solution, best possible 137537.98 (1232.07 seconds)\n", + "Cbc0010I After 80000 nodes, 15272 on tree, 140249.17 best solution, best possible 137537.98 (1233.25 seconds)\n", + "Cbc0010I After 80100 nodes, 15274 on tree, 140249.17 best solution, best possible 137537.98 (1234.28 seconds)\n", + "Cbc0010I After 80200 nodes, 15273 on tree, 140249.17 best solution, best possible 137537.98 (1235.48 seconds)\n", + "Cbc0010I After 80300 nodes, 15279 on tree, 140249.17 best solution, best possible 137537.98 (1236.52 seconds)\n", + "Cbc0010I After 80400 nodes, 15272 on tree, 140249.17 best solution, best possible 137537.98 (1237.90 seconds)\n", + "Cbc0010I After 80500 nodes, 15284 on tree, 140249.17 best solution, best possible 137537.98 (1239.08 seconds)\n", + "Cbc0010I After 80600 nodes, 15282 on tree, 140249.17 best solution, best possible 137537.98 (1240.56 seconds)\n", + "Cbc0010I After 80700 nodes, 15274 on tree, 140249.17 best solution, best possible 137537.98 (1241.98 seconds)\n", + "Cbc0010I After 80800 nodes, 15271 on tree, 140249.17 best solution, best possible 137537.98 (1243.25 seconds)\n", + "Cbc0010I After 80900 nodes, 15277 on tree, 140249.17 best solution, best possible 137537.98 (1244.82 seconds)\n", + "Cbc0010I After 81000 nodes, 15292 on tree, 140249.17 best solution, best possible 137537.98 (1246.27 seconds)\n", + "Cbc0010I After 81100 nodes, 15289 on tree, 140249.17 best solution, best possible 137537.98 (1247.74 seconds)\n", + "Cbc0010I After 81200 nodes, 15279 on tree, 140249.17 best solution, best possible 137537.98 (1249.17 seconds)\n", + "Cbc0010I After 81300 nodes, 15287 on tree, 140249.17 best solution, best possible 137537.98 (1251.03 seconds)\n", + "Cbc0010I After 81400 nodes, 15275 on tree, 140249.17 best solution, best possible 137537.98 (1253.12 seconds)\n", + "Cbc0010I After 81500 nodes, 15271 on tree, 140249.17 best solution, best possible 137537.98 (1255.04 seconds)\n", + "Cbc0010I After 81600 nodes, 15274 on tree, 140249.17 best solution, best possible 137537.98 (1256.50 seconds)\n", + "Cbc0010I After 81700 nodes, 15263 on tree, 140249.17 best solution, best possible 137537.98 (1257.37 seconds)\n", + "Cbc0010I After 81800 nodes, 15269 on tree, 140249.17 best solution, best possible 137537.98 (1258.37 seconds)\n", + "Cbc0010I After 81900 nodes, 15266 on tree, 140249.17 best solution, best possible 137537.98 (1259.33 seconds)\n", + "Cbc0010I After 82000 nodes, 15268 on tree, 140249.17 best solution, best possible 137537.98 (1260.20 seconds)\n", + "Cbc0010I After 82100 nodes, 15213 on tree, 140249.17 best solution, best possible 137537.98 (1261.09 seconds)\n", + "Cbc0010I After 82200 nodes, 15205 on tree, 140249.17 best solution, best possible 137537.98 (1261.91 seconds)\n", + "Cbc0010I After 82300 nodes, 15194 on tree, 140249.17 best solution, best possible 137537.98 (1262.95 seconds)\n", + "Cbc0010I After 82400 nodes, 15164 on tree, 140249.17 best solution, best possible 137537.98 (1264.02 seconds)\n", + "Cbc0010I After 82500 nodes, 15163 on tree, 140249.17 best solution, best possible 137537.98 (1265.51 seconds)\n", + "Cbc0010I After 82600 nodes, 15157 on tree, 140249.17 best solution, best possible 137537.98 (1267.02 seconds)\n", + "Cbc0010I After 82700 nodes, 15159 on tree, 140249.17 best solution, best possible 137537.98 (1268.67 seconds)\n", + "Cbc0010I After 82800 nodes, 15157 on tree, 140249.17 best solution, best possible 137537.98 (1270.00 seconds)\n", + "Cbc0010I After 82900 nodes, 15150 on tree, 140249.17 best solution, best possible 137537.98 (1271.21 seconds)\n", + "Cbc0010I After 83000 nodes, 15145 on tree, 140249.17 best solution, best possible 137537.98 (1272.61 seconds)\n", + "Cbc0010I After 83100 nodes, 15147 on tree, 140249.17 best solution, best possible 137537.98 (1273.46 seconds)\n", + "Cbc0010I After 83200 nodes, 15147 on tree, 140249.17 best solution, best possible 137537.98 (1274.58 seconds)\n", + "Cbc0010I After 83300 nodes, 15154 on tree, 140249.17 best solution, best possible 137537.98 (1275.22 seconds)\n", + "Cbc0010I After 83400 nodes, 15150 on tree, 140249.17 best solution, best possible 137537.98 (1276.00 seconds)\n", + "Cbc0010I After 83500 nodes, 15147 on tree, 140249.17 best solution, best possible 137537.98 (1277.12 seconds)\n", + "Cbc0010I After 83600 nodes, 15143 on tree, 140249.17 best solution, best possible 137537.98 (1278.99 seconds)\n", + "Cbc0010I After 83700 nodes, 15144 on tree, 140249.17 best solution, best possible 137537.98 (1280.94 seconds)\n", + "Cbc0010I After 83800 nodes, 15157 on tree, 140249.17 best solution, best possible 137537.98 (1282.46 seconds)\n", + "Cbc0010I After 83900 nodes, 15156 on tree, 140249.17 best solution, best possible 137537.98 (1283.64 seconds)\n", + "Cbc0010I After 84000 nodes, 15151 on tree, 140249.17 best solution, best possible 137537.98 (1284.72 seconds)\n", + "Cbc0010I After 84100 nodes, 15154 on tree, 140249.17 best solution, best possible 137537.98 (1286.32 seconds)\n", + "Cbc0010I After 84200 nodes, 15152 on tree, 140249.17 best solution, best possible 137537.98 (1287.74 seconds)\n", + "Cbc0010I After 84300 nodes, 15152 on tree, 140249.17 best solution, best possible 137537.98 (1289.28 seconds)\n", + "Cbc0010I After 84400 nodes, 15149 on tree, 140249.17 best solution, best possible 137537.98 (1290.48 seconds)\n", + "Cbc0010I After 84500 nodes, 15152 on tree, 140249.17 best solution, best possible 137537.98 (1293.37 seconds)\n", + "Cbc0010I After 84600 nodes, 15151 on tree, 140249.17 best solution, best possible 137537.98 (1294.58 seconds)\n", + "Cbc0010I After 84700 nodes, 15146 on tree, 140249.17 best solution, best possible 137537.98 (1295.67 seconds)\n", + "Cbc0010I After 84800 nodes, 15154 on tree, 140249.17 best solution, best possible 137537.98 (1297.15 seconds)\n", + "Cbc0010I After 84900 nodes, 15156 on tree, 140249.17 best solution, best possible 137537.98 (1297.94 seconds)\n", + "Cbc0010I After 85000 nodes, 15156 on tree, 140249.17 best solution, best possible 137537.98 (1298.66 seconds)\n", + "Cbc0010I After 85100 nodes, 15149 on tree, 140249.17 best solution, best possible 137537.98 (1299.59 seconds)\n", + "Cbc0010I After 85200 nodes, 15147 on tree, 140249.17 best solution, best possible 137537.98 (1300.91 seconds)\n", + "Cbc0010I After 85300 nodes, 15149 on tree, 140249.17 best solution, best possible 137537.98 (1302.22 seconds)\n", + "Cbc0010I After 85400 nodes, 15150 on tree, 140249.17 best solution, best possible 137537.98 (1303.64 seconds)\n", + "Cbc0010I After 85500 nodes, 15146 on tree, 140249.17 best solution, best possible 137537.98 (1305.83 seconds)\n", + "Cbc0010I After 85600 nodes, 15152 on tree, 140249.17 best solution, best possible 137537.98 (1306.97 seconds)\n", + "Cbc0010I After 85700 nodes, 15155 on tree, 140249.17 best solution, best possible 137537.98 (1308.31 seconds)\n", + "Cbc0010I After 85800 nodes, 15146 on tree, 140249.17 best solution, best possible 137537.98 (1309.72 seconds)\n", + "Cbc0010I After 85900 nodes, 15149 on tree, 140249.17 best solution, best possible 137537.98 (1311.12 seconds)\n", + "Cbc0010I After 86000 nodes, 15156 on tree, 140249.17 best solution, best possible 137537.98 (1312.53 seconds)\n", + "Cbc0010I After 86100 nodes, 15153 on tree, 140249.17 best solution, best possible 137537.98 (1313.62 seconds)\n", + "Cbc0010I After 86200 nodes, 15153 on tree, 140249.17 best solution, best possible 137537.98 (1314.54 seconds)\n", + "Cbc0010I After 86300 nodes, 15150 on tree, 140249.17 best solution, best possible 137537.98 (1315.59 seconds)\n", + "Cbc0010I After 86400 nodes, 15149 on tree, 140249.17 best solution, best possible 137537.98 (1316.75 seconds)\n", + "Cbc0010I After 86500 nodes, 15151 on tree, 140249.17 best solution, best possible 137537.98 (1319.38 seconds)\n", + "Cbc0010I After 86600 nodes, 15148 on tree, 140249.17 best solution, best possible 137537.98 (1320.45 seconds)\n", + "Cbc0010I After 86700 nodes, 15146 on tree, 140249.17 best solution, best possible 137537.98 (1321.72 seconds)\n", + "Cbc0010I After 86800 nodes, 15150 on tree, 140249.17 best solution, best possible 137537.98 (1323.23 seconds)\n", + "Cbc0010I After 86900 nodes, 15150 on tree, 140249.17 best solution, best possible 137537.98 (1324.64 seconds)\n", + "Cbc0010I After 87000 nodes, 15145 on tree, 140249.17 best solution, best possible 137537.98 (1326.02 seconds)\n", + "Cbc0010I After 87100 nodes, 15145 on tree, 140249.17 best solution, best possible 137537.98 (1327.51 seconds)\n", + "Cbc0010I After 87200 nodes, 15133 on tree, 140249.17 best solution, best possible 137537.98 (1328.85 seconds)\n", + "Cbc0010I After 87300 nodes, 15115 on tree, 140249.17 best solution, best possible 137537.98 (1330.02 seconds)\n", + "Cbc0010I After 87400 nodes, 15080 on tree, 140249.17 best solution, best possible 137537.98 (1331.96 seconds)\n", + "Cbc0010I After 87500 nodes, 15063 on tree, 140249.17 best solution, best possible 137537.98 (1332.95 seconds)\n", + "Cbc0010I After 87600 nodes, 15060 on tree, 140249.17 best solution, best possible 137537.98 (1334.12 seconds)\n", + "Cbc0010I After 87700 nodes, 15044 on tree, 140249.17 best solution, best possible 137537.98 (1335.29 seconds)\n", + "Cbc0010I After 87800 nodes, 15031 on tree, 140249.17 best solution, best possible 137537.98 (1336.35 seconds)\n", + "Cbc0010I After 87900 nodes, 15025 on tree, 140249.17 best solution, best possible 137537.98 (1337.47 seconds)\n", + "Cbc0010I After 88000 nodes, 15020 on tree, 140249.17 best solution, best possible 137537.98 (1338.65 seconds)\n", + "Cbc0010I After 88100 nodes, 15020 on tree, 140249.17 best solution, best possible 137537.98 (1340.02 seconds)\n", + "Cbc0010I After 88200 nodes, 15018 on tree, 140249.17 best solution, best possible 137537.98 (1340.85 seconds)\n", + "Cbc0010I After 88300 nodes, 15017 on tree, 140249.17 best solution, best possible 137537.98 (1342.08 seconds)\n", + "Cbc0010I After 88400 nodes, 15017 on tree, 140249.17 best solution, best possible 137537.98 (1344.24 seconds)\n", + "Cbc0010I After 88500 nodes, 15015 on tree, 140249.17 best solution, best possible 137537.98 (1345.67 seconds)\n", + "Cbc0010I After 88600 nodes, 15026 on tree, 140249.17 best solution, best possible 137537.98 (1346.95 seconds)\n", + "Cbc0010I After 88700 nodes, 15020 on tree, 140249.17 best solution, best possible 137537.98 (1347.92 seconds)\n", + "Cbc0010I After 88800 nodes, 15017 on tree, 140249.17 best solution, best possible 137537.98 (1349.06 seconds)\n", + "Cbc0010I After 88900 nodes, 15020 on tree, 140249.17 best solution, best possible 137537.98 (1350.42 seconds)\n", + "Cbc0010I After 89000 nodes, 15017 on tree, 140249.17 best solution, best possible 137537.98 (1351.53 seconds)\n", + "Cbc0010I After 89100 nodes, 15020 on tree, 140249.17 best solution, best possible 137537.98 (1352.99 seconds)\n", + "Cbc0010I After 89200 nodes, 15019 on tree, 140249.17 best solution, best possible 137537.98 (1353.61 seconds)\n", + "Cbc0010I After 89300 nodes, 15023 on tree, 140249.17 best solution, best possible 137537.98 (1355.14 seconds)\n", + "Cbc0010I After 89400 nodes, 15024 on tree, 140249.17 best solution, best possible 137537.98 (1357.80 seconds)\n", + "Cbc0010I After 89500 nodes, 15024 on tree, 140249.17 best solution, best possible 137537.98 (1359.53 seconds)\n", + "Cbc0010I After 89600 nodes, 15023 on tree, 140249.17 best solution, best possible 137537.98 (1360.87 seconds)\n", + "Cbc0010I After 89700 nodes, 15021 on tree, 140249.17 best solution, best possible 137537.98 (1362.05 seconds)\n", + "Cbc0010I After 89800 nodes, 15021 on tree, 140249.17 best solution, best possible 137537.98 (1364.09 seconds)\n", + "Cbc0010I After 89900 nodes, 15015 on tree, 140249.17 best solution, best possible 137537.98 (1365.47 seconds)\n", + "Cbc0010I After 90000 nodes, 15021 on tree, 140249.17 best solution, best possible 137537.98 (1367.04 seconds)\n", + "Cbc0012I Integer solution of 140205.73 found by heuristic after 5573209 iterations and 90056 nodes (1367.94 seconds)\n", + "Cbc0010I After 90100 nodes, 15017 on tree, 140205.73 best solution, best possible 137537.98 (1369.71 seconds)\n", + "Cbc0010I After 90200 nodes, 15065 on tree, 140205.73 best solution, best possible 137537.98 (1372.67 seconds)\n", + "Cbc0010I After 90300 nodes, 15114 on tree, 140205.73 best solution, best possible 137537.98 (1374.94 seconds)\n", + "Cbc0010I After 90400 nodes, 15165 on tree, 140205.73 best solution, best possible 137537.98 (1377.79 seconds)\n", + "Cbc0010I After 90500 nodes, 15213 on tree, 140205.73 best solution, best possible 137537.98 (1380.25 seconds)\n", + "Cbc0010I After 90600 nodes, 15261 on tree, 140205.73 best solution, best possible 137537.98 (1383.66 seconds)\n", + "Cbc0010I After 90700 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1386.27 seconds)\n", + "Cbc0010I After 90800 nodes, 15361 on tree, 140205.73 best solution, best possible 137537.98 (1388.30 seconds)\n", + "Cbc0010I After 90900 nodes, 15409 on tree, 140205.73 best solution, best possible 137537.98 (1390.88 seconds)\n", + "Cbc0010I After 91000 nodes, 15456 on tree, 140205.73 best solution, best possible 137537.98 (1394.32 seconds)\n", + "Cbc0010I After 91100 nodes, 15458 on tree, 140205.73 best solution, best possible 137537.98 (1397.03 seconds)\n", + "Cbc0010I After 91200 nodes, 15453 on tree, 140205.73 best solution, best possible 137537.98 (1399.51 seconds)\n", + "Cbc0010I After 91300 nodes, 15450 on tree, 140205.73 best solution, best possible 137537.98 (1401.16 seconds)\n", + "Cbc0010I After 91400 nodes, 15461 on tree, 140205.73 best solution, best possible 137537.98 (1403.09 seconds)\n", + "Cbc0010I After 91500 nodes, 15463 on tree, 140205.73 best solution, best possible 137537.98 (1404.92 seconds)\n", + "Cbc0010I After 91600 nodes, 15452 on tree, 140205.73 best solution, best possible 137537.98 (1406.61 seconds)\n", + "Cbc0010I After 91700 nodes, 15448 on tree, 140205.73 best solution, best possible 137537.98 (1409.35 seconds)\n", + "Cbc0010I After 91800 nodes, 15450 on tree, 140205.73 best solution, best possible 137537.98 (1411.04 seconds)\n", + "Cbc0010I After 91900 nodes, 15445 on tree, 140205.73 best solution, best possible 137537.98 (1412.66 seconds)\n", + "Cbc0010I After 92000 nodes, 15450 on tree, 140205.73 best solution, best possible 137537.98 (1413.91 seconds)\n", + "Cbc0010I After 92100 nodes, 15450 on tree, 140205.73 best solution, best possible 137537.98 (1415.48 seconds)\n", + "Cbc0010I After 92200 nodes, 15453 on tree, 140205.73 best solution, best possible 137537.98 (1417.04 seconds)\n", + "Cbc0010I After 92300 nodes, 15453 on tree, 140205.73 best solution, best possible 137537.98 (1417.68 seconds)\n", + "Cbc0010I After 92400 nodes, 15455 on tree, 140205.73 best solution, best possible 137537.98 (1418.76 seconds)\n", + "Cbc0010I After 92500 nodes, 15455 on tree, 140205.73 best solution, best possible 137537.98 (1419.83 seconds)\n", + "Cbc0010I After 92600 nodes, 15461 on tree, 140205.73 best solution, best possible 137537.98 (1421.53 seconds)\n", + "Cbc0010I After 92700 nodes, 15460 on tree, 140205.73 best solution, best possible 137537.98 (1422.57 seconds)\n", + "Cbc0010I After 92800 nodes, 15451 on tree, 140205.73 best solution, best possible 137537.98 (1423.40 seconds)\n", + "Cbc0010I After 92900 nodes, 15452 on tree, 140205.73 best solution, best possible 137537.98 (1424.18 seconds)\n", + "Cbc0010I After 93000 nodes, 15448 on tree, 140205.73 best solution, best possible 137537.98 (1425.34 seconds)\n", + "Cbc0010I After 93100 nodes, 15453 on tree, 140205.73 best solution, best possible 137537.98 (1427.20 seconds)\n", + "Cbc0010I After 93200 nodes, 15452 on tree, 140205.73 best solution, best possible 137537.98 (1428.89 seconds)\n", + "Cbc0010I After 93300 nodes, 15449 on tree, 140205.73 best solution, best possible 137537.98 (1430.64 seconds)\n", + "Cbc0010I After 93400 nodes, 15450 on tree, 140205.73 best solution, best possible 137537.98 (1432.44 seconds)\n", + "Cbc0010I After 93500 nodes, 15445 on tree, 140205.73 best solution, best possible 137537.98 (1434.80 seconds)\n", + "Cbc0010I After 93600 nodes, 15449 on tree, 140205.73 best solution, best possible 137537.98 (1436.48 seconds)\n", + "Cbc0010I After 93700 nodes, 15445 on tree, 140205.73 best solution, best possible 137537.98 (1438.51 seconds)\n", + "Cbc0010I After 93800 nodes, 15450 on tree, 140205.73 best solution, best possible 137537.98 (1440.09 seconds)\n", + "Cbc0010I After 93900 nodes, 15450 on tree, 140205.73 best solution, best possible 137537.98 (1441.28 seconds)\n", + "Cbc0010I After 94000 nodes, 15447 on tree, 140205.73 best solution, best possible 137537.98 (1442.56 seconds)\n", + "Cbc0010I After 94100 nodes, 15451 on tree, 140205.73 best solution, best possible 137537.98 (1444.27 seconds)\n", + "Cbc0010I After 94200 nodes, 15450 on tree, 140205.73 best solution, best possible 137537.98 (1446.22 seconds)\n", + "Cbc0010I After 94300 nodes, 15451 on tree, 140205.73 best solution, best possible 137537.98 (1447.91 seconds)\n", + "Cbc0010I After 94400 nodes, 15410 on tree, 140205.73 best solution, best possible 137537.98 (1448.90 seconds)\n", + "Cbc0010I After 94500 nodes, 15386 on tree, 140205.73 best solution, best possible 137537.98 (1449.72 seconds)\n", + "Cbc0010I After 94600 nodes, 15346 on tree, 140205.73 best solution, best possible 137537.98 (1451.10 seconds)\n", + "Cbc0010I After 94700 nodes, 15340 on tree, 140205.73 best solution, best possible 137537.98 (1452.67 seconds)\n", + "Cbc0010I After 94800 nodes, 15335 on tree, 140205.73 best solution, best possible 137537.98 (1453.85 seconds)\n", + "Cbc0010I After 94900 nodes, 15339 on tree, 140205.73 best solution, best possible 137537.98 (1454.62 seconds)\n", + "Cbc0010I After 95000 nodes, 15330 on tree, 140205.73 best solution, best possible 137537.98 (1455.40 seconds)\n", + "Cbc0010I After 95100 nodes, 15329 on tree, 140205.73 best solution, best possible 137537.98 (1456.66 seconds)\n", + "Cbc0010I After 95200 nodes, 15322 on tree, 140205.73 best solution, best possible 137537.98 (1457.83 seconds)\n", + "Cbc0010I After 95300 nodes, 15319 on tree, 140205.73 best solution, best possible 137537.98 (1460.23 seconds)\n", + "Cbc0010I After 95400 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1461.82 seconds)\n", + "Cbc0010I After 95500 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1463.34 seconds)\n", + "Cbc0010I After 95600 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1464.37 seconds)\n", + "Cbc0010I After 95700 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1465.64 seconds)\n", + "Cbc0010I After 95800 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1466.69 seconds)\n", + "Cbc0010I After 95900 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1467.97 seconds)\n", + "Cbc0010I After 96000 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1469.42 seconds)\n", + "Cbc0010I After 96100 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1470.82 seconds)\n", + "Cbc0010I After 96200 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1473.65 seconds)\n", + "Cbc0010I After 96300 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1475.25 seconds)\n", + "Cbc0010I After 96400 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1477.00 seconds)\n", + "Cbc0010I After 96500 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1478.11 seconds)\n", + "Cbc0010I After 96600 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1480.05 seconds)\n", + "Cbc0010I After 96700 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1481.93 seconds)\n", + "Cbc0010I After 96800 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1483.48 seconds)\n", + "Cbc0010I After 96900 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1486.08 seconds)\n", + "Cbc0010I After 97000 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1487.55 seconds)\n", + "Cbc0010I After 97100 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1488.67 seconds)\n", + "Cbc0010I After 97200 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1489.86 seconds)\n", + "Cbc0010I After 97300 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1490.90 seconds)\n", + "Cbc0010I After 97400 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1492.03 seconds)\n", + "Cbc0010I After 97500 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1493.31 seconds)\n", + "Cbc0010I After 97600 nodes, 15315 on tree, 140205.73 best solution, best possible 137537.98 (1494.36 seconds)\n", + "Cbc0010I After 97700 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1495.43 seconds)\n", + "Cbc0010I After 97800 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1496.66 seconds)\n", + "Cbc0010I After 97900 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1499.23 seconds)\n", + "Cbc0010I After 98000 nodes, 15321 on tree, 140205.73 best solution, best possible 137537.98 (1500.13 seconds)\n", + "Cbc0010I After 98100 nodes, 15316 on tree, 140205.73 best solution, best possible 137537.98 (1500.88 seconds)\n", + "Cbc0010I After 98200 nodes, 15318 on tree, 140205.73 best solution, best possible 137537.98 (1501.60 seconds)\n", + "Cbc0010I After 98300 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1502.30 seconds)\n", + "Cbc0010I After 98400 nodes, 15315 on tree, 140205.73 best solution, best possible 137537.98 (1503.22 seconds)\n", + "Cbc0010I After 98500 nodes, 15318 on tree, 140205.73 best solution, best possible 137537.98 (1504.01 seconds)\n", + "Cbc0010I After 98600 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1504.85 seconds)\n", + "Cbc0010I After 98700 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1505.66 seconds)\n", + "Cbc0010I After 98800 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1506.53 seconds)\n", + "Cbc0010I After 98900 nodes, 15315 on tree, 140205.73 best solution, best possible 137537.98 (1507.74 seconds)\n", + "Cbc0010I After 99000 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1508.70 seconds)\n", + "Cbc0010I After 99100 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1510.86 seconds)\n", + "Cbc0010I After 99200 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1512.54 seconds)\n", + "Cbc0010I After 99300 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1513.68 seconds)\n", + "Cbc0010I After 99400 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1514.92 seconds)\n", + "Cbc0010I After 99500 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1516.76 seconds)\n", + "Cbc0010I After 99600 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1518.48 seconds)\n", + "Cbc0010I After 99700 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1520.02 seconds)\n", + "Cbc0010I After 99800 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1521.46 seconds)\n", + "Cbc0010I After 99900 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1524.18 seconds)\n", + "Cbc0010I After 100000 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1526.91 seconds)\n", + "Cbc0010I After 100100 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1528.91 seconds)\n", + "Cbc0010I After 100200 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1530.67 seconds)\n", + "Cbc0010I After 100300 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1532.43 seconds)\n", + "Cbc0010I After 100400 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1534.11 seconds)\n", + "Cbc0010I After 100500 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1536.19 seconds)\n", + "Cbc0010I After 100600 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1538.48 seconds)\n", + "Cbc0010I After 100700 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1540.48 seconds)\n", + "Cbc0010I After 100800 nodes, 15298 on tree, 140205.73 best solution, best possible 137537.98 (1542.03 seconds)\n", + "Cbc0010I After 100900 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1543.43 seconds)\n", + "Cbc0010I After 101000 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1544.59 seconds)\n", + "Cbc0010I After 101100 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1545.98 seconds)\n", + "Cbc0010I After 101200 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1547.03 seconds)\n", + "Cbc0010I After 101300 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1548.23 seconds)\n", + "Cbc0010I After 101400 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1550.17 seconds)\n", + "Cbc0010I After 101500 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1552.07 seconds)\n", + "Cbc0010I After 101600 nodes, 15299 on tree, 140205.73 best solution, best possible 137537.98 (1553.32 seconds)\n", + "Cbc0010I After 101700 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1554.89 seconds)\n", + "Cbc0010I After 101800 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1556.23 seconds)\n", + "Cbc0010I After 101900 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1556.99 seconds)\n", + "Cbc0010I After 102000 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1558.10 seconds)\n", + "Cbc0010I After 102100 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1559.50 seconds)\n", + "Cbc0010I After 102200 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1560.38 seconds)\n", + "Cbc0010I After 102300 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1562.54 seconds)\n", + "Cbc0010I After 102400 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1564.68 seconds)\n", + "Cbc0010I After 102500 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1566.09 seconds)\n", + "Cbc0010I After 102600 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1566.95 seconds)\n", + "Cbc0010I After 102700 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1568.22 seconds)\n", + "Cbc0010I After 102800 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1570.03 seconds)\n", + "Cbc0010I After 102900 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1572.24 seconds)\n", + "Cbc0010I After 103000 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1576.54 seconds)\n", + "Cbc0010I After 103100 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1579.03 seconds)\n", + "Cbc0010I After 103200 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1581.52 seconds)\n", + "Cbc0010I After 103300 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1583.48 seconds)\n", + "Cbc0010I After 103400 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1585.19 seconds)\n", + "Cbc0010I After 103500 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1587.69 seconds)\n", + "Cbc0010I After 103600 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1590.34 seconds)\n", + "Cbc0010I After 103700 nodes, 15299 on tree, 140205.73 best solution, best possible 137537.98 (1591.70 seconds)\n", + "Cbc0010I After 103800 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1593.35 seconds)\n", + "Cbc0010I After 103900 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1594.91 seconds)\n", + "Cbc0010I After 104000 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1596.41 seconds)\n", + "Cbc0010I After 104100 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1598.33 seconds)\n", + "Cbc0010I After 104200 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1599.96 seconds)\n", + "Cbc0010I After 104300 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1602.56 seconds)\n", + "Cbc0010I After 104400 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1604.32 seconds)\n", + "Cbc0010I After 104500 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1606.13 seconds)\n", + "Cbc0010I After 104600 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1608.38 seconds)\n", + "Cbc0010I After 104700 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1610.22 seconds)\n", + "Cbc0010I After 104800 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1611.45 seconds)\n", + "Cbc0010I After 104900 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1612.72 seconds)\n", + "Cbc0010I After 105000 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1614.94 seconds)\n", + "Cbc0010I After 105100 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1616.70 seconds)\n", + "Cbc0010I After 105200 nodes, 15318 on tree, 140205.73 best solution, best possible 137537.98 (1618.17 seconds)\n", + "Cbc0010I After 105300 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1619.13 seconds)\n", + "Cbc0010I After 105400 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1620.07 seconds)\n", + "Cbc0010I After 105500 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1621.54 seconds)\n", + "Cbc0010I After 105600 nodes, 15316 on tree, 140205.73 best solution, best possible 137537.98 (1622.49 seconds)\n", + "Cbc0010I After 105700 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1623.41 seconds)\n", + "Cbc0010I After 105800 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1624.60 seconds)\n", + "Cbc0010I After 105900 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1627.32 seconds)\n", + "Cbc0010I After 106000 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1629.16 seconds)\n", + "Cbc0010I After 106100 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1630.41 seconds)\n", + "Cbc0010I After 106200 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1631.75 seconds)\n", + "Cbc0010I After 106300 nodes, 15315 on tree, 140205.73 best solution, best possible 137537.98 (1633.75 seconds)\n", + "Cbc0010I After 106400 nodes, 15324 on tree, 140205.73 best solution, best possible 137537.98 (1635.85 seconds)\n", + "Cbc0010I After 106500 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1638.00 seconds)\n", + "Cbc0010I After 106600 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1640.73 seconds)\n", + "Cbc0010I After 106700 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1642.47 seconds)\n", + "Cbc0010I After 106800 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1644.16 seconds)\n", + "Cbc0010I After 106900 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1645.75 seconds)\n", + "Cbc0010I After 107000 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1647.21 seconds)\n", + "Cbc0010I After 107100 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1648.79 seconds)\n", + "Cbc0010I After 107200 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1650.27 seconds)\n", + "Cbc0010I After 107300 nodes, 15315 on tree, 140205.73 best solution, best possible 137537.98 (1651.68 seconds)\n", + "Cbc0010I After 107400 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1653.05 seconds)\n", + "Cbc0010I After 107500 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1654.66 seconds)\n", + "Cbc0010I After 107600 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1655.46 seconds)\n", + "Cbc0010I After 107700 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1656.44 seconds)\n", + "Cbc0010I After 107800 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1657.31 seconds)\n", + "Cbc0010I After 107900 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1658.45 seconds)\n", + "Cbc0010I After 108000 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1660.04 seconds)\n", + "Cbc0010I After 108100 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1661.52 seconds)\n", + "Cbc0010I After 108200 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1663.08 seconds)\n", + "Cbc0010I After 108300 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1664.63 seconds)\n", + "Cbc0010I After 108400 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1667.34 seconds)\n", + "Cbc0010I After 108500 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1669.24 seconds)\n", + "Cbc0010I After 108600 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1671.17 seconds)\n", + "Cbc0010I After 108700 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1673.05 seconds)\n", + "Cbc0010I After 108800 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1674.44 seconds)\n", + "Cbc0010I After 108900 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1675.81 seconds)\n", + "Cbc0010I After 109000 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1677.17 seconds)\n", + "Cbc0010I After 109100 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1679.11 seconds)\n", + "Cbc0010I After 109200 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1681.13 seconds)\n", + "Cbc0010I After 109300 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1682.37 seconds)\n", + "Cbc0010I After 109400 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1683.25 seconds)\n", + "Cbc0010I After 109500 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1684.15 seconds)\n", + "Cbc0010I After 109600 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1685.08 seconds)\n", + "Cbc0010I After 109700 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1686.02 seconds)\n", + "Cbc0010I After 109800 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1686.70 seconds)\n", + "Cbc0010I After 109900 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1688.94 seconds)\n", + "Cbc0010I After 110000 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1690.84 seconds)\n", + "Cbc0010I After 110100 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1694.09 seconds)\n", + "Cbc0010I After 110200 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1696.16 seconds)\n", + "Cbc0010I After 110300 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1698.30 seconds)\n", + "Cbc0010I After 110400 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1700.57 seconds)\n", + "Cbc0010I After 110500 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1703.35 seconds)\n", + "Cbc0010I After 110600 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1706.63 seconds)\n", + "Cbc0010I After 110700 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1708.65 seconds)\n", + "Cbc0010I After 110800 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1711.41 seconds)\n", + "Cbc0010I After 110900 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1713.57 seconds)\n", + "Cbc0010I After 111000 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1716.11 seconds)\n", + "Cbc0010I After 111100 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1719.79 seconds)\n", + "Cbc0010I After 111200 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1721.70 seconds)\n", + "Cbc0010I After 111300 nodes, 15315 on tree, 140205.73 best solution, best possible 137537.98 (1724.47 seconds)\n", + "Cbc0010I After 111400 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1726.33 seconds)\n", + "Cbc0010I After 111500 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1728.61 seconds)\n", + "Cbc0010I After 111600 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1732.61 seconds)\n", + "Cbc0010I After 111700 nodes, 15320 on tree, 140205.73 best solution, best possible 137537.98 (1735.43 seconds)\n", + "Cbc0010I After 111800 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1737.30 seconds)\n", + "Cbc0010I After 111900 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1739.14 seconds)\n", + "Cbc0010I After 112000 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1740.87 seconds)\n", + "Cbc0010I After 112100 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1744.57 seconds)\n", + "Cbc0010I After 112200 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1747.35 seconds)\n", + "Cbc0010I After 112300 nodes, 15324 on tree, 140205.73 best solution, best possible 137537.98 (1748.37 seconds)\n", + "Cbc0010I After 112400 nodes, 15321 on tree, 140205.73 best solution, best possible 137537.98 (1749.66 seconds)\n", + "Cbc0010I After 112500 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1752.12 seconds)\n", + "Cbc0010I After 112600 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1754.14 seconds)\n", + "Cbc0010I After 112700 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1756.41 seconds)\n", + "Cbc0010I After 112800 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1758.60 seconds)\n", + "Cbc0010I After 112900 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1759.72 seconds)\n", + "Cbc0010I After 113000 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1760.97 seconds)\n", + "Cbc0010I After 113100 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1762.31 seconds)\n", + "Cbc0010I After 113200 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1763.53 seconds)\n", + "Cbc0010I After 113300 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1764.73 seconds)\n", + "Cbc0010I After 113400 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1765.85 seconds)\n", + "Cbc0010I After 113500 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1767.63 seconds)\n", + "Cbc0010I After 113600 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1768.87 seconds)\n", + "Cbc0010I After 113700 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1770.86 seconds)\n", + "Cbc0010I After 113800 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1771.90 seconds)\n", + "Cbc0010I After 113900 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1773.15 seconds)\n", + "Cbc0010I After 114000 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1774.32 seconds)\n", + "Cbc0010I After 114100 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1775.06 seconds)\n", + "Cbc0010I After 114200 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1776.06 seconds)\n", + "Cbc0010I After 114300 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1777.22 seconds)\n", + "Cbc0010I After 114400 nodes, 15317 on tree, 140205.73 best solution, best possible 137537.98 (1778.18 seconds)\n", + "Cbc0010I After 114500 nodes, 15321 on tree, 140205.73 best solution, best possible 137537.98 (1778.95 seconds)\n", + "Cbc0010I After 114600 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1779.70 seconds)\n", + "Cbc0010I After 114700 nodes, 15316 on tree, 140205.73 best solution, best possible 137537.98 (1780.54 seconds)\n", + "Cbc0010I After 114800 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1781.43 seconds)\n", + "Cbc0010I After 114900 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1783.43 seconds)\n", + "Cbc0010I After 115000 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1784.92 seconds)\n", + "Cbc0010I After 115100 nodes, 15316 on tree, 140205.73 best solution, best possible 137537.98 (1786.47 seconds)\n", + "Cbc0010I After 115200 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1787.56 seconds)\n", + "Cbc0010I After 115300 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1788.80 seconds)\n", + "Cbc0010I After 115400 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1790.13 seconds)\n", + "Cbc0010I After 115500 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1791.43 seconds)\n", + "Cbc0010I After 115600 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1792.52 seconds)\n", + "Cbc0010I After 115700 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1794.24 seconds)\n", + "Cbc0010I After 115800 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1796.48 seconds)\n", + "Cbc0010I After 115900 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1797.61 seconds)\n", + "Cbc0010I After 116000 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1798.95 seconds)\n", + "Cbc0010I After 116100 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1800.29 seconds)\n", + "Cbc0010I After 116200 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1801.85 seconds)\n", + "Cbc0010I After 116300 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1803.38 seconds)\n", + "Cbc0010I After 116400 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1805.06 seconds)\n", + "Cbc0010I After 116500 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1806.48 seconds)\n", + "Cbc0010I After 116600 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1808.39 seconds)\n", + "Cbc0010I After 116700 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1810.72 seconds)\n", + "Cbc0010I After 116800 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1812.35 seconds)\n", + "Cbc0010I After 116900 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1813.46 seconds)\n", + "Cbc0010I After 117000 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1814.37 seconds)\n", + "Cbc0010I After 117100 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1815.50 seconds)\n", + "Cbc0010I After 117200 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1816.75 seconds)\n", + "Cbc0010I After 117300 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1817.76 seconds)\n", + "Cbc0010I After 117400 nodes, 15323 on tree, 140205.73 best solution, best possible 137537.98 (1820.14 seconds)\n", + "Cbc0010I After 117500 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1823.41 seconds)\n", + "Cbc0010I After 117600 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1825.02 seconds)\n", + "Cbc0010I After 117700 nodes, 15317 on tree, 140205.73 best solution, best possible 137537.98 (1827.05 seconds)\n", + "Cbc0010I After 117800 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1829.15 seconds)\n", + "Cbc0010I After 117900 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1831.25 seconds)\n", + "Cbc0010I After 118000 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1833.41 seconds)\n", + "Cbc0010I After 118100 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1836.66 seconds)\n", + "Cbc0010I After 118200 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1839.03 seconds)\n", + "Cbc0010I After 118300 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1841.02 seconds)\n", + "Cbc0010I After 118400 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1843.08 seconds)\n", + "Cbc0010I After 118500 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1845.29 seconds)\n", + "Cbc0010I After 118600 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1847.58 seconds)\n", + "Cbc0010I After 118700 nodes, 15315 on tree, 140205.73 best solution, best possible 137537.98 (1849.17 seconds)\n", + "Cbc0010I After 118800 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1850.27 seconds)\n", + "Cbc0010I After 118900 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1851.36 seconds)\n", + "Cbc0010I After 119000 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1852.48 seconds)\n", + "Cbc0010I After 119100 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1853.87 seconds)\n", + "Cbc0010I After 119200 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1855.23 seconds)\n", + "Cbc0010I After 119300 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1857.88 seconds)\n", + "Cbc0010I After 119400 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1860.46 seconds)\n", + "Cbc0010I After 119500 nodes, 15315 on tree, 140205.73 best solution, best possible 137537.98 (1862.60 seconds)\n", + "Cbc0010I After 119600 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1864.10 seconds)\n", + "Cbc0010I After 119700 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1865.52 seconds)\n", + "Cbc0010I After 119800 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1867.63 seconds)\n", + "Cbc0010I After 119900 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1869.29 seconds)\n", + "Cbc0010I After 120000 nodes, 15316 on tree, 140205.73 best solution, best possible 137537.98 (1871.06 seconds)\n", + "Cbc0010I After 120100 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1873.11 seconds)\n", + "Cbc0010I After 120200 nodes, 15313 on tree, 140205.73 best solution, best possible 137537.98 (1875.40 seconds)\n", + "Cbc0010I After 120300 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1876.79 seconds)\n", + "Cbc0010I After 120400 nodes, 15310 on tree, 140205.73 best solution, best possible 137537.98 (1878.84 seconds)\n", + "Cbc0010I After 120500 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1880.76 seconds)\n", + "Cbc0010I After 120600 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1882.49 seconds)\n", + "Cbc0010I After 120700 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1884.13 seconds)\n", + "Cbc0010I After 120800 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1886.53 seconds)\n", + "Cbc0010I After 120900 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1888.37 seconds)\n", + "Cbc0010I After 121000 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1889.84 seconds)\n", + "Cbc0010I After 121100 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1891.26 seconds)\n", + "Cbc0010I After 121200 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1893.13 seconds)\n", + "Cbc0010I After 121300 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1895.08 seconds)\n", + "Cbc0010I After 121400 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1898.23 seconds)\n", + "Cbc0010I After 121500 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1901.13 seconds)\n", + "Cbc0010I After 121600 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1902.56 seconds)\n", + "Cbc0010I After 121700 nodes, 15299 on tree, 140205.73 best solution, best possible 137537.98 (1904.28 seconds)\n", + "Cbc0010I After 121800 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1905.63 seconds)\n", + "Cbc0010I After 121900 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1906.61 seconds)\n", + "Cbc0010I After 122000 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1907.36 seconds)\n", + "Cbc0010I After 122100 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1908.72 seconds)\n", + "Cbc0010I After 122200 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1909.95 seconds)\n", + "Cbc0010I After 122300 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1912.17 seconds)\n", + "Cbc0010I After 122400 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1913.73 seconds)\n", + "Cbc0010I After 122500 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1914.67 seconds)\n", + "Cbc0010I After 122600 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1915.54 seconds)\n", + "Cbc0010I After 122700 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1916.66 seconds)\n", + "Cbc0010I After 122800 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1918.12 seconds)\n", + "Cbc0010I After 122900 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1919.67 seconds)\n", + "Cbc0010I After 123000 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1920.59 seconds)\n", + "Cbc0010I After 123100 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1921.69 seconds)\n", + "Cbc0010I After 123200 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1922.60 seconds)\n", + "Cbc0010I After 123300 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1923.39 seconds)\n", + "Cbc0010I After 123400 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1926.18 seconds)\n", + "Cbc0010I After 123500 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1927.48 seconds)\n", + "Cbc0010I After 123600 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1928.57 seconds)\n", + "Cbc0010I After 123700 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1929.40 seconds)\n", + "Cbc0010I After 123800 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1930.41 seconds)\n", + "Cbc0010I After 123900 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1931.70 seconds)\n", + "Cbc0010I After 124000 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1933.53 seconds)\n", + "Cbc0010I After 124100 nodes, 15314 on tree, 140205.73 best solution, best possible 137537.98 (1934.75 seconds)\n", + "Cbc0010I After 124200 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1936.13 seconds)\n", + "Cbc0010I After 124300 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1939.07 seconds)\n", + "Cbc0010I After 124400 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1940.18 seconds)\n", + "Cbc0010I After 124500 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1941.23 seconds)\n", + "Cbc0010I After 124600 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1943.20 seconds)\n", + "Cbc0010I After 124700 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1945.14 seconds)\n", + "Cbc0010I After 124800 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1947.49 seconds)\n", + "Cbc0010I After 124900 nodes, 15307 on tree, 140205.73 best solution, best possible 137537.98 (1949.86 seconds)\n", + "Cbc0010I After 125000 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1952.99 seconds)\n", + "Cbc0010I After 125100 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1954.61 seconds)\n", + "Cbc0010I After 125200 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1956.38 seconds)\n", + "Cbc0010I After 125300 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1957.83 seconds)\n", + "Cbc0010I After 125400 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1958.84 seconds)\n", + "Cbc0010I After 125500 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1959.98 seconds)\n", + "Cbc0010I After 125600 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1960.96 seconds)\n", + "Cbc0010I After 125700 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1962.21 seconds)\n", + "Cbc0010I After 125800 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1963.91 seconds)\n", + "Cbc0010I After 125900 nodes, 15311 on tree, 140205.73 best solution, best possible 137537.98 (1965.28 seconds)\n", + "Cbc0010I After 126000 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1966.17 seconds)\n", + "Cbc0010I After 126100 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1967.07 seconds)\n", + "Cbc0010I After 126200 nodes, 15312 on tree, 140205.73 best solution, best possible 137537.98 (1968.47 seconds)\n", + "Cbc0010I After 126300 nodes, 15315 on tree, 140205.73 best solution, best possible 137537.98 (1969.30 seconds)\n", + "Cbc0010I After 126400 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1970.22 seconds)\n", + "Cbc0010I After 126500 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1971.29 seconds)\n", + "Cbc0010I After 126600 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1972.37 seconds)\n", + "Cbc0010I After 126700 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1973.96 seconds)\n", + "Cbc0010I After 126800 nodes, 15305 on tree, 140205.73 best solution, best possible 137537.98 (1974.67 seconds)\n", + "Cbc0010I After 126900 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1976.61 seconds)\n", + "Cbc0010I After 127000 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1978.27 seconds)\n", + "Cbc0010I After 127100 nodes, 15300 on tree, 140205.73 best solution, best possible 137537.98 (1979.65 seconds)\n", + "Cbc0010I After 127200 nodes, 15308 on tree, 140205.73 best solution, best possible 137537.98 (1981.12 seconds)\n", + "Cbc0010I After 127300 nodes, 15315 on tree, 140205.73 best solution, best possible 137537.98 (1982.31 seconds)\n", + "Cbc0010I After 127400 nodes, 15306 on tree, 140205.73 best solution, best possible 137537.98 (1983.33 seconds)\n", + "Cbc0010I After 127500 nodes, 15301 on tree, 140205.73 best solution, best possible 137537.98 (1984.28 seconds)\n", + "Cbc0010I After 127600 nodes, 15303 on tree, 140205.73 best solution, best possible 137537.98 (1985.68 seconds)\n", + "Cbc0010I After 127700 nodes, 15304 on tree, 140205.73 best solution, best possible 137537.98 (1987.25 seconds)\n", + "Cbc0010I After 127800 nodes, 15298 on tree, 140205.73 best solution, best possible 137537.98 (1989.71 seconds)\n", + "Cbc0010I After 127900 nodes, 15309 on tree, 140205.73 best solution, best possible 137537.98 (1993.10 seconds)\n", + "Cbc0010I After 128000 nodes, 15298 on tree, 140205.73 best solution, best possible 137537.98 (1994.60 seconds)\n", + "Cbc0010I After 128100 nodes, 15299 on tree, 140205.73 best solution, best possible 137537.98 (1995.88 seconds)\n", + "Cbc0010I After 128200 nodes, 15296 on tree, 140205.73 best solution, best possible 137537.98 (1996.89 seconds)\n", + "Cbc0010I After 128300 nodes, 15302 on tree, 140205.73 best solution, best possible 137537.98 (1997.79 seconds)\n", + "Cbc0010I After 128400 nodes, 15293 on tree, 140205.73 best solution, best possible 137537.98 (1998.71 seconds)\n", + "Cbc0030I Thread 0 used 64237 times, waiting to start 19.803477, 359094 locks, 28.041971 locked, 1.6001887 waiting for locks\n", + "Cbc0030I Thread 1 used 64199 times, waiting to start 20.596667, 359551 locks, 28.986487 locked, 1.6104231 waiting for locks\n", + "Cbc0030I Main thread 1971.1934 waiting for threads, 258303 locks, 1.4486723 locked, 0.90268469 waiting for locks\n", + "Cbc0020I Exiting on maximum time\n", + "Cbc0005I Partial search - best objective 140205.73 (best possible 137537.98), took 7597483 iterations and 128435 nodes (2001.07 seconds)\n", + "Cbc0032I Strong branching done 136144 times (4474514 iterations), fathomed 6326 nodes and fixed 29932 variables\n", + "Cbc0035I Maximum depth 80, 2079754 variables fixed on reduced cost\n", + "Cuts at root node changed objective from 135593 to 136601\n", + "Probing was tried 45 times and created 12 cuts of which 0 were active after adding rounds of cuts (0.225 seconds)\n", + "Gomory was tried 39170 times and created 18100 cuts of which 0 were active after adding rounds of cuts (209.879 seconds)\n", + "Knapsack was tried 45 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.041 seconds)\n", + "Clique was tried 45 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.007 seconds)\n", + "MixedIntegerRounding2 was tried 45 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.114 seconds)\n", + "FlowCover was tried 39179 times and created 264475 cuts of which 0 were active after adding rounds of cuts (51.057 seconds)\n", + "TwoMirCuts was tried 39170 times and created 31663 cuts of which 0 were active after adding rounds of cuts (66.195 seconds)\n", + "ZeroHalf was tried 45 times and created 60 cuts of which 0 were active after adding rounds of cuts (0.264 seconds)\n", + "\n", + "Result - Stopped on time limit\n", + "\n", + "Objective value: 140205.72777396\n", + "Lower bound: 137537.976\n", + "Gap: 0.02\n", + "Enumerated nodes: 128435\n", + "Total iterations: 7597483\n", + "Time (CPU seconds): 3468.86\n", + "Time (Wallclock seconds): 2001.12\n", + "\n", + "Total time (CPU seconds): 3468.89 (Wallclock seconds): 2001.17\n", + "\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "WARNING:pyomo.core:Loading a SolverResults object with an 'aborted' status, but containing a solution\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "wfn.plot()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 445 + }, + "id": "QLQlkHK2rPzM", + "outputId": "12108061-99ab-4a24-bbd5-e6a023c48786" + }, + "execution_count": 15, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ], + "image/png": "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\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "print(round(cost), ' €')" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ViglorCarVlB", + "outputId": "057d4409-a43c-44ea-d06d-2303cb9e3fce" + }, + "execution_count": 16, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "40717295 €\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Turbine positions from IEA37 Borssele Irregular System\n", + "turbine_pos = np.array([[ 501266.54317574, 493922.24954271, 502907.19014959,\n", + " 502095.12557912, 496804.65625979, 485102.77027462,\n", + " 490588.37581976, 502784.83623495, 503032.60103984,\n", + " 484578.18744373, 486707.11173729, 492087.72419315,\n", + " 502245.04987847, 486993.72883541, 486339.8895555 ,\n", + " 501937.23428743, 490866.23972975, 489583.97960791,\n", + " 493244.9730643 , 484184.85247367, 495837.28295403,\n", + " 501763.67866768, 497805.65064534, 502520.00847477,\n", + " 488196.97665934, 502261.66468538, 487660.64327449,\n", + " 496326.9935416 , 495560.72737215, 492442.84358768,\n", + " 489811.54071237, 488384.83348118, 485719.30135799,\n", + " 499693.18157517, 494872.64094661, 491373.54080591,\n", + " 492179.32192035, 497223.78998917, 497331.96509787,\n", + " 496443.58939824, 499696.09772372, 497791.49735685,\n", + " 503157.65666188, 502656.8554572 , 499013.99779544,\n", + " 502389.74235056, 499568.71665005, 498412.30882307,\n", + " 500126.55758186, 493699.48183891, 500599.36370223,\n", + " 497855.63257599, 495278.26879751, 493700.84819603,\n", + " 492224.36209737, 498554.39258276, 489085.25534532,\n", + " 500588.51971806, 499488.70435074, 494285.31394068,\n", + " 495302.17515336, 498899.58556002, 493038.70804906,\n", + " 494794.71182856, 498335.26945098, 499450.50531967,\n", + " 501551.75352904, 499153.2151173 , 489443.95885503,\n", + " 495667.21147171, 499425.06290419, 497613.73273252,\n", + " 501417.31149458, 490809.4125091 ],\n", + " [5715995.89368689, 5723126.80050036, 5727399.9805506 ,\n", + " 5721783.86274859, 5720326.91432108, 5731616.65356541,\n", + " 5726358.98043443, 5726554.75877663, 5728282.55126408,\n", + " 5732113.07451875, 5733271.99003168, 5734978.77939308,\n", + " 5722819.59436817, 5729811.23565774, 5730437.7600403 ,\n", + " 5720684.82136608, 5734593.97542309, 5734183.81996312,\n", + " 5735342.15989407, 5732482.14974234, 5721263.84408409,\n", + " 5719468.98024809, 5719362.35078842, 5724757.97736015,\n", + " 5733753.39470066, 5729567.10859905, 5729175.80279442,\n", + " 5736318.33484618, 5730365.5374425 , 5732709.48377899,\n", + " 5727115.20095509, 5728488.33293297, 5731024.64807625,\n", + " 5726888.85185297, 5722211.95206308, 5725600.69245181,\n", + " 5724815.96578192, 5723028.55637662, 5736641.50567781,\n", + " 5729036.22360224, 5730763.14987881, 5727794.82805826,\n", + " 5729153.34310612, 5725664.84960664, 5718720.22921093,\n", + " 5723814.40501551, 5717641.93281177, 5736984.27770828,\n", + " 5737528.57233063, 5728053.24974494, 5716646.9421783 ,\n", + " 5725266.2923445 , 5735992.35112698, 5731256.8927141 ,\n", + " 5729156.69983083, 5731292.34250883, 5727820.39671998,\n", + " 5730350.50472903, 5736258.41812358, 5735679.58364592,\n", + " 5733057.09366068, 5735074.06082225, 5723984.10312561,\n", + " 5726569.83357447, 5733940.1526999 , 5737313.57272465,\n", + " 5718018.11318477, 5721395.33171942, 5731966.4812102 ,\n", + " 5725151.81249156, 5723990.57679841, 5732484.17798327,\n", + " 5729964.36557568, 5730586.99083753]])" + ], + "metadata": { + "id": "SRf25Ywh343d" + }, + "execution_count": 17, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "site_info = {'site_name': 'IEA-37 Irregular',\n", + " 'handle': 'iea37irreg',\n", + " 'boundary': BoundaryC\n", + " }" + ], + "metadata": { + "id": "3C1YZmqz4F8I" + }, + "execution_count": 18, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "wfn = WindFarmNetwork(turbine_positions=turbine_pos,\n", + " substation_positions=substation_pos,\n", + " drivers=[InterArrayDriver(**interarray_setting)],\n", + " sequence=[0],\n", + " cables=cables,\n", + " site_info=site_info)" + ], + "metadata": { + "id": "3VYEoS2H4Gi4" + }, + "execution_count": 19, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "cost, state = wfn.design()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "N5Wz1_zA4MbD", + "outputId": "98e3243e-0064-4a3d-dede-68fc7427529e" + }, + "execution_count": 20, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "The solver is available\n", + "<edge_crossing> discarding ('bn', 'Z'): would cross [('M', 'bo')]\n", + "Solving \"IEA-37 Irregular\", k = k\n", + "\n", + "Welcome to the CBC MILP Solver \n", + "Version: 2.10.7 \n", + "Build Date: Feb 14 2022 \n", + "\n", + "command line - /usr/bin/cbc -ratio 0.002 -seconds 2000 -timeMode elapsed -threads 2 -printingOptions all -import /tmp/tmps39qp3nz.pyomo.lp -mipstart /tmp/tmpbfn91sah.cbc.soln -stat=1 -solve -solu /tmp/tmps39qp3nz.pyomo.soln (default strategy 1)\n", + "ratioGap was changed from 0 to 0.002\n", + "seconds was changed from 1e+100 to 2000\n", + "Option for timeMode changed from cpu to elapsed\n", + "threads was changed from 0 to 2\n", + "Option for printingOptions changed from normal to all\n", + "opening mipstart file /tmp/tmpbfn91sah.cbc.soln.\n", + "MIPStart values read for 148 variables.\n", + "Presolve 2666 (-83) rows, 1136 (-8) columns and 8450 (-622) elements\n", + "Statistics for presolved model\n", + "Original problem has 1144 integers (572 of which binary)\n", + "Presolved problem has 1136 integers (564 of which binary)\n", + "==== 572 zero objective 344 different\n", + "==== absolute objective values 344 different\n", + "==== for integers 572 zero objective 344 different\n", + "==== for integers absolute objective values 344 different\n", + "===== end objective counts\n", + "\n", + "\n", + "Problem has 2666 rows, 1136 columns (564 with objective) and 8450 elements\n", + "Column breakdown:\n", + "0 of type 0.0->inf, 572 of type 0.0->up, 0 of type lo->inf, \n", + "0 of type lo->up, 0 of type free, 0 of type fixed, \n", + "0 of type -inf->0.0, 0 of type -inf->up, 564 of type 0.0->1.0 \n", + "Row breakdown:\n", + "0 of type E 0.0, 140 of type E 1.0, 0 of type E -1.0, \n", + "1 of type E other, 0 of type G 0.0, 0 of type G 1.0, \n", + "1 of type G other, 1136 of type L 0.0, 1298 of type L 1.0, \n", + "90 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other, \n", + "0 of type Free \n", + "Continuous objective value is 124045 - 0.04 seconds\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 890 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 672 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 277 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 101 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 40 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 21 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 3 strengthened rows, 0 substitutions\n", + "Cgl0004I processed model has 2311 rows, 1144 columns (1144 integer (572 of which binary)) and 9122 elements\n", + "Cbc0045I MIPStart provided solution with cost 148101\n", + "Cbc0012I Integer solution of 148100.69 found by Reduced search after 0 iterations and 0 nodes (0.41 seconds)\n", + "Cbc0038I Full problem 2311 rows 1144 columns, reduced to 332 rows 175 columns\n", + "Cbc0031I 57 added rows had average density of 145.73684\n", + "Cbc0013I At root node, 57 cuts changed objective from 127486.15 to 130202.79 in 20 passes\n", + "Cbc0014I Cut generator 0 (Probing) - 1 row cuts average 5.0 elements, 0 column cuts (0 active) in 0.182 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 1 (Gomory) - 242 row cuts average 382.1 elements, 0 column cuts (0 active) in 0.500 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 2 (Knapsack) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.041 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.008 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 3 row cuts average 98.7 elements, 0 column cuts (0 active) in 0.143 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 5 (FlowCover) - 94 row cuts average 11.7 elements, 0 column cuts (0 active) in 0.043 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 6 (TwoMirCuts) - 254 row cuts average 218.8 elements, 0 column cuts (0 active) in 0.188 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 7 (ZeroHalf) - 15 row cuts average 11.6 elements, 0 column cuts (0 active) in 0.295 seconds - new frequency is -100\n", + "Cbc0010I After 0 nodes, 1 on tree, 148100.69 best solution, best possible 130202.79 (2.56 seconds)\n", + "Cbc0010I After 100 nodes, 54 on tree, 148100.69 best solution, best possible 130268.15 (11.18 seconds)\n", + "Cbc0010I After 200 nodes, 92 on tree, 148100.69 best solution, best possible 130268.15 (11.69 seconds)\n", + "Cbc0010I After 300 nodes, 109 on tree, 148100.69 best solution, best possible 130268.15 (12.08 seconds)\n", + "Cbc0010I After 400 nodes, 128 on tree, 148100.69 best solution, best possible 130268.15 (12.44 seconds)\n", + "Cbc0010I After 500 nodes, 124 on tree, 148100.69 best solution, best possible 130268.15 (12.82 seconds)\n", + "Cbc0010I After 600 nodes, 116 on tree, 148100.69 best solution, best possible 130268.15 (13.44 seconds)\n", + "Cbc0010I After 700 nodes, 121 on tree, 148100.69 best solution, best possible 130268.15 (14.39 seconds)\n", + "Cbc0010I After 800 nodes, 123 on tree, 148100.69 best solution, best possible 130268.15 (15.55 seconds)\n", + "Cbc0010I After 900 nodes, 123 on tree, 148100.69 best solution, best possible 130268.15 (17.21 seconds)\n", + "Cbc0010I After 1000 nodes, 118 on tree, 148100.69 best solution, best possible 130268.15 (19.51 seconds)\n", + "Cbc0010I After 1100 nodes, 121 on tree, 148100.69 best solution, best possible 130268.15 (20.82 seconds)\n", + "Cbc0010I After 1200 nodes, 123 on tree, 148100.69 best solution, best possible 130268.15 (21.77 seconds)\n", + "Cbc0012I Integer solution of 147979.24 found by heuristic after 45236 iterations and 1265 nodes (22.56 seconds)\n", + "Cbc0012I Integer solution of 147734.56 found by heuristic after 45270 iterations and 1266 nodes (22.58 seconds)\n", + "Cbc0010I After 1300 nodes, 143 on tree, 147734.56 best solution, best possible 130268.15 (23.34 seconds)\n", + "Cbc0010I After 1400 nodes, 191 on tree, 147734.56 best solution, best possible 130268.15 (25.24 seconds)\n", + "Cbc0010I After 1500 nodes, 242 on tree, 147734.56 best solution, best possible 130268.15 (26.72 seconds)\n", + "Cbc0010I After 1600 nodes, 286 on tree, 147734.56 best solution, best possible 130268.15 (28.20 seconds)\n", + "Cbc0010I After 1700 nodes, 331 on tree, 147734.56 best solution, best possible 130268.15 (29.92 seconds)\n", + "Cbc0010I After 1800 nodes, 377 on tree, 147734.56 best solution, best possible 130268.15 (32.24 seconds)\n", + "Cbc0010I After 1900 nodes, 428 on tree, 147734.56 best solution, best possible 130268.15 (33.63 seconds)\n", + "Cbc0010I After 2000 nodes, 476 on tree, 147734.56 best solution, best possible 130268.15 (34.66 seconds)\n", + "Cbc0010I After 2100 nodes, 521 on tree, 147734.56 best solution, best possible 130268.15 (35.75 seconds)\n", + "Cbc0010I After 2200 nodes, 568 on tree, 147734.56 best solution, best possible 130268.15 (36.76 seconds)\n", + "Cbc0010I After 2300 nodes, 617 on tree, 147734.56 best solution, best possible 130268.15 (37.80 seconds)\n", + "Cbc0010I After 2400 nodes, 667 on tree, 147734.56 best solution, best possible 130268.15 (38.99 seconds)\n", + "Cbc0010I After 2500 nodes, 716 on tree, 147734.56 best solution, best possible 130268.15 (40.01 seconds)\n", + "Cbc0010I After 2600 nodes, 765 on tree, 147734.56 best solution, best possible 130268.15 (40.89 seconds)\n", + "Cbc0010I After 2700 nodes, 815 on tree, 147734.56 best solution, best possible 130268.15 (41.77 seconds)\n", + "Cbc0010I After 2800 nodes, 863 on tree, 147734.56 best solution, best possible 130268.15 (42.89 seconds)\n", + "Cbc0012I Integer solution of 139123.24 found by heuristic after 115755 iterations and 2889 nodes (44.16 seconds)\n", + "Cbc0010I After 2900 nodes, 496 on tree, 139123.24 best solution, best possible 130268.15 (44.41 seconds)\n", + "Cbc0010I After 3000 nodes, 542 on tree, 139123.24 best solution, best possible 130268.15 (46.40 seconds)\n", + "Cbc0010I After 3100 nodes, 587 on tree, 139123.24 best solution, best possible 130268.15 (47.85 seconds)\n", + "Cbc0010I After 3200 nodes, 622 on tree, 139123.24 best solution, best possible 130268.15 (49.18 seconds)\n", + "Cbc0010I After 3300 nodes, 662 on tree, 139123.24 best solution, best possible 130268.15 (50.54 seconds)\n", + "Cbc0010I After 3400 nodes, 707 on tree, 139123.24 best solution, best possible 130268.15 (51.55 seconds)\n", + "Cbc0010I After 3500 nodes, 751 on tree, 139123.24 best solution, best possible 130268.15 (52.61 seconds)\n", + "Cbc0010I After 3600 nodes, 792 on tree, 139123.24 best solution, best possible 130268.15 (53.61 seconds)\n", + "Cbc0010I After 3700 nodes, 834 on tree, 139123.24 best solution, best possible 130268.15 (54.70 seconds)\n", + "Cbc0010I After 3800 nodes, 880 on tree, 139123.24 best solution, best possible 130268.15 (55.94 seconds)\n", + "Cbc0010I After 3900 nodes, 928 on tree, 139123.24 best solution, best possible 130268.15 (57.70 seconds)\n", + "Cbc0010I After 4000 nodes, 972 on tree, 139123.24 best solution, best possible 130268.15 (59.13 seconds)\n", + "Cbc0010I After 4100 nodes, 1016 on tree, 139123.24 best solution, best possible 130268.15 (60.18 seconds)\n", + "Cbc0010I After 4200 nodes, 1059 on tree, 139123.24 best solution, best possible 130268.15 (61.37 seconds)\n", + "Cbc0010I After 4300 nodes, 1105 on tree, 139123.24 best solution, best possible 130268.15 (62.45 seconds)\n", + "Cbc0010I After 4400 nodes, 1151 on tree, 139123.24 best solution, best possible 130268.15 (63.23 seconds)\n", + "Cbc0010I After 4500 nodes, 1199 on tree, 139123.24 best solution, best possible 130268.15 (64.04 seconds)\n", + "Cbc0010I After 4600 nodes, 1244 on tree, 139123.24 best solution, best possible 130268.15 (65.14 seconds)\n", + "Cbc0010I After 4700 nodes, 1287 on tree, 139123.24 best solution, best possible 130268.15 (66.12 seconds)\n", + "Cbc0010I After 4800 nodes, 1333 on tree, 139123.24 best solution, best possible 130268.15 (67.03 seconds)\n", + "Cbc0010I After 4900 nodes, 1376 on tree, 139123.24 best solution, best possible 130268.15 (68.14 seconds)\n", + "Cbc0010I After 5000 nodes, 1414 on tree, 139123.24 best solution, best possible 130268.15 (69.70 seconds)\n", + "Cbc0010I After 5100 nodes, 1454 on tree, 139123.24 best solution, best possible 130268.15 (71.28 seconds)\n", + "Cbc0010I After 5200 nodes, 1495 on tree, 139123.24 best solution, best possible 130268.15 (72.27 seconds)\n", + "Cbc0010I After 5300 nodes, 1540 on tree, 139123.24 best solution, best possible 130268.15 (73.20 seconds)\n", + "Cbc0010I After 5400 nodes, 1578 on tree, 139123.24 best solution, best possible 130268.15 (74.12 seconds)\n", + "Cbc0010I After 5500 nodes, 1625 on tree, 139123.24 best solution, best possible 130268.15 (75.00 seconds)\n", + "Cbc0010I After 5600 nodes, 1666 on tree, 139123.24 best solution, best possible 130268.15 (76.08 seconds)\n", + "Cbc0010I After 5700 nodes, 1711 on tree, 139123.24 best solution, best possible 130268.15 (77.13 seconds)\n", + "Cbc0010I After 5800 nodes, 1756 on tree, 139123.24 best solution, best possible 130268.15 (78.09 seconds)\n", + "Cbc0010I After 5900 nodes, 1803 on tree, 139123.24 best solution, best possible 130268.15 (79.28 seconds)\n", + "Cbc0010I After 6000 nodes, 1841 on tree, 139123.24 best solution, best possible 130268.15 (80.31 seconds)\n", + "Cbc0010I After 6100 nodes, 1878 on tree, 139123.24 best solution, best possible 130268.15 (81.25 seconds)\n", + "Cbc0010I After 6200 nodes, 1917 on tree, 139123.24 best solution, best possible 130268.15 (82.65 seconds)\n", + "Cbc0010I After 6300 nodes, 1965 on tree, 139123.24 best solution, best possible 130268.15 (84.32 seconds)\n", + "Cbc0010I After 6400 nodes, 2011 on tree, 139123.24 best solution, best possible 130268.15 (85.48 seconds)\n", + "Cbc0010I After 6500 nodes, 2053 on tree, 139123.24 best solution, best possible 130268.15 (86.24 seconds)\n", + "Cbc0010I After 6600 nodes, 2099 on tree, 139123.24 best solution, best possible 130268.15 (87.11 seconds)\n", + "Cbc0010I After 6700 nodes, 2143 on tree, 139123.24 best solution, best possible 130268.15 (88.14 seconds)\n", + "Cbc0010I After 6800 nodes, 2190 on tree, 139123.24 best solution, best possible 130268.15 (89.54 seconds)\n", + "Cbc0010I After 6900 nodes, 2241 on tree, 139123.24 best solution, best possible 130268.15 (90.79 seconds)\n", + "Cbc0010I After 7000 nodes, 2289 on tree, 139123.24 best solution, best possible 130268.15 (91.96 seconds)\n", + "Cbc0010I After 7100 nodes, 2336 on tree, 139123.24 best solution, best possible 130268.15 (93.01 seconds)\n", + "Cbc0010I After 7200 nodes, 2382 on tree, 139123.24 best solution, best possible 130268.15 (94.06 seconds)\n", + "Cbc0010I After 7300 nodes, 2428 on tree, 139123.24 best solution, best possible 130268.15 (95.23 seconds)\n", + "Cbc0010I After 7400 nodes, 2472 on tree, 139123.24 best solution, best possible 130268.15 (97.08 seconds)\n", + "Cbc0010I After 7500 nodes, 2521 on tree, 139123.24 best solution, best possible 130268.15 (98.13 seconds)\n", + "Cbc0010I After 7600 nodes, 2561 on tree, 139123.24 best solution, best possible 130268.15 (99.27 seconds)\n", + "Cbc0010I After 7700 nodes, 2604 on tree, 139123.24 best solution, best possible 130268.15 (100.17 seconds)\n", + "Cbc0010I After 7800 nodes, 2649 on tree, 139123.24 best solution, best possible 130268.15 (101.24 seconds)\n", + "Cbc0010I After 7900 nodes, 2688 on tree, 139123.24 best solution, best possible 130268.15 (102.08 seconds)\n", + "Cbc0010I After 8000 nodes, 2726 on tree, 139123.24 best solution, best possible 130268.15 (103.02 seconds)\n", + "Cbc0010I After 8100 nodes, 2771 on tree, 139123.24 best solution, best possible 130268.15 (104.00 seconds)\n", + "Cbc0010I After 8200 nodes, 2819 on tree, 139123.24 best solution, best possible 130268.15 (104.82 seconds)\n", + "Cbc0010I After 8300 nodes, 2867 on tree, 139123.24 best solution, best possible 130268.15 (105.58 seconds)\n", + "Cbc0010I After 8400 nodes, 2912 on tree, 139123.24 best solution, best possible 130268.15 (106.35 seconds)\n", + "Cbc0010I After 8500 nodes, 2955 on tree, 139123.24 best solution, best possible 130268.15 (107.50 seconds)\n", + "Cbc0010I After 8600 nodes, 2997 on tree, 139123.24 best solution, best possible 130268.15 (108.99 seconds)\n", + "Cbc0010I After 8700 nodes, 3043 on tree, 139123.24 best solution, best possible 130268.15 (110.68 seconds)\n", + "Cbc0010I After 8800 nodes, 3087 on tree, 139123.24 best solution, best possible 130268.15 (111.81 seconds)\n", + "Cbc0010I After 8900 nodes, 3135 on tree, 139123.24 best solution, best possible 130268.15 (112.82 seconds)\n", + "Cbc0010I After 9000 nodes, 3178 on tree, 139123.24 best solution, best possible 130268.15 (113.80 seconds)\n", + "Cbc0010I After 9100 nodes, 3217 on tree, 139123.24 best solution, best possible 130268.15 (114.69 seconds)\n", + "Cbc0010I After 9200 nodes, 3260 on tree, 139123.24 best solution, best possible 130268.15 (115.58 seconds)\n", + "Cbc0010I After 9300 nodes, 3306 on tree, 139123.24 best solution, best possible 130268.15 (116.95 seconds)\n", + "Cbc0010I After 9400 nodes, 3348 on tree, 139123.24 best solution, best possible 130268.15 (117.96 seconds)\n", + "Cbc0010I After 9500 nodes, 3388 on tree, 139123.24 best solution, best possible 130268.15 (118.97 seconds)\n", + "Cbc0010I After 9600 nodes, 3430 on tree, 139123.24 best solution, best possible 130268.15 (119.96 seconds)\n", + "Cbc0010I After 9700 nodes, 3472 on tree, 139123.24 best solution, best possible 130268.15 (121.59 seconds)\n", + "Cbc0010I After 9800 nodes, 3518 on tree, 139123.24 best solution, best possible 130268.15 (122.92 seconds)\n", + "Cbc0010I After 9900 nodes, 3559 on tree, 139123.24 best solution, best possible 130268.15 (124.01 seconds)\n", + "Cbc0010I After 10000 nodes, 3607 on tree, 139123.24 best solution, best possible 130268.15 (125.03 seconds)\n", + "Cbc0010I After 10100 nodes, 3648 on tree, 139123.24 best solution, best possible 130268.15 (125.84 seconds)\n", + "Cbc0010I After 10200 nodes, 3684 on tree, 139123.24 best solution, best possible 130268.15 (126.87 seconds)\n", + "Cbc0010I After 10300 nodes, 3722 on tree, 139123.24 best solution, best possible 130268.15 (127.92 seconds)\n", + "Cbc0010I After 10400 nodes, 3768 on tree, 139123.24 best solution, best possible 130268.15 (129.37 seconds)\n", + "Cbc0010I After 10500 nodes, 3809 on tree, 139123.24 best solution, best possible 130268.15 (130.81 seconds)\n", + "Cbc0010I After 10600 nodes, 3856 on tree, 139123.24 best solution, best possible 130268.15 (132.76 seconds)\n", + "Cbc0010I After 10700 nodes, 3902 on tree, 139123.24 best solution, best possible 130268.15 (134.45 seconds)\n", + "Cbc0010I After 10800 nodes, 3943 on tree, 139123.24 best solution, best possible 130268.15 (135.75 seconds)\n", + "Cbc0010I After 10900 nodes, 3981 on tree, 139123.24 best solution, best possible 130268.15 (136.76 seconds)\n", + "Cbc0010I After 11000 nodes, 4024 on tree, 139123.24 best solution, best possible 130268.15 (137.73 seconds)\n", + "Cbc0010I After 11100 nodes, 4071 on tree, 139123.24 best solution, best possible 130706.47 (144.57 seconds)\n", + "Cbc0010I After 11200 nodes, 4120 on tree, 139123.24 best solution, best possible 130811.3 (148.15 seconds)\n", + "Cbc0010I After 11300 nodes, 4170 on tree, 139123.24 best solution, best possible 130876.5 (151.49 seconds)\n", + "Cbc0010I After 11400 nodes, 4220 on tree, 139123.24 best solution, best possible 130925.97 (154.44 seconds)\n", + "Cbc0010I After 11500 nodes, 4270 on tree, 139123.24 best solution, best possible 130977.48 (157.18 seconds)\n", + "Cbc0010I After 11600 nodes, 4320 on tree, 139123.24 best solution, best possible 131015.19 (159.56 seconds)\n", + "Cbc0010I After 11700 nodes, 4370 on tree, 139123.24 best solution, best possible 131067.34 (162.27 seconds)\n", + "Cbc0010I After 11800 nodes, 4419 on tree, 139123.24 best solution, best possible 131101.24 (164.09 seconds)\n", + "Cbc0010I After 11900 nodes, 4469 on tree, 139123.24 best solution, best possible 131136.44 (166.02 seconds)\n", + "Cbc0010I After 12000 nodes, 4519 on tree, 139123.24 best solution, best possible 131162.94 (167.65 seconds)\n", + "Cbc0010I After 12100 nodes, 4570 on tree, 139123.24 best solution, best possible 131184.44 (169.47 seconds)\n", + "Cbc0010I After 12200 nodes, 4620 on tree, 139123.24 best solution, best possible 131205.76 (171.59 seconds)\n", + "Cbc0010I After 12300 nodes, 4670 on tree, 139123.24 best solution, best possible 131225.81 (174.48 seconds)\n", + "Cbc0010I After 12400 nodes, 4719 on tree, 139123.24 best solution, best possible 131244.66 (176.45 seconds)\n", + "Cbc0010I After 12500 nodes, 4768 on tree, 139123.24 best solution, best possible 131263.18 (178.20 seconds)\n", + "Cbc0010I After 12600 nodes, 4817 on tree, 139123.24 best solution, best possible 131280.66 (180.18 seconds)\n", + "Cbc0010I After 12700 nodes, 4867 on tree, 139123.24 best solution, best possible 131296.11 (182.14 seconds)\n", + "Cbc0010I After 12800 nodes, 4917 on tree, 139123.24 best solution, best possible 131313.25 (183.98 seconds)\n", + "Cbc0010I After 12900 nodes, 4966 on tree, 139123.24 best solution, best possible 131328.52 (186.90 seconds)\n", + "Cbc0010I After 13000 nodes, 5014 on tree, 139123.24 best solution, best possible 131346.32 (188.87 seconds)\n", + "Cbc0010I After 13100 nodes, 5063 on tree, 139123.24 best solution, best possible 131346.54 (190.39 seconds)\n", + "Cbc0010I After 13200 nodes, 5108 on tree, 139123.24 best solution, best possible 131346.54 (192.03 seconds)\n", + "Cbc0010I After 13300 nodes, 5153 on tree, 139123.24 best solution, best possible 131346.54 (193.29 seconds)\n", + "Cbc0010I After 13400 nodes, 5200 on tree, 139123.24 best solution, best possible 131346.54 (194.81 seconds)\n", + "Cbc0010I After 13500 nodes, 5247 on tree, 139123.24 best solution, best possible 131346.54 (196.47 seconds)\n", + "Cbc0010I After 13600 nodes, 5298 on tree, 139123.24 best solution, best possible 131346.54 (198.42 seconds)\n", + "Cbc0010I After 13700 nodes, 5343 on tree, 139123.24 best solution, best possible 131346.54 (200.54 seconds)\n", + "Cbc0010I After 13800 nodes, 5387 on tree, 139123.24 best solution, best possible 131346.54 (201.80 seconds)\n", + "Cbc0010I After 13900 nodes, 5432 on tree, 139123.24 best solution, best possible 131346.54 (203.15 seconds)\n", + "Cbc0010I After 14000 nodes, 5479 on tree, 139123.24 best solution, best possible 131346.54 (204.49 seconds)\n", + "Cbc0010I After 14100 nodes, 5529 on tree, 139123.24 best solution, best possible 131362.83 (206.18 seconds)\n", + "Cbc0010I After 14200 nodes, 5578 on tree, 139123.24 best solution, best possible 131376.35 (208.16 seconds)\n", + "Cbc0010I After 14300 nodes, 5627 on tree, 139123.24 best solution, best possible 131392.59 (210.07 seconds)\n", + "Cbc0010I After 14400 nodes, 5676 on tree, 139123.24 best solution, best possible 131406.13 (212.87 seconds)\n", + "Cbc0010I After 14500 nodes, 5726 on tree, 139123.24 best solution, best possible 131415.66 (214.95 seconds)\n", + "Cbc0010I After 14600 nodes, 5776 on tree, 139123.24 best solution, best possible 131426.68 (216.42 seconds)\n", + "Cbc0010I After 14700 nodes, 5825 on tree, 139123.24 best solution, best possible 131436.81 (218.04 seconds)\n", + "Cbc0010I After 14800 nodes, 5874 on tree, 139123.24 best solution, best possible 131445.78 (219.69 seconds)\n", + "Cbc0010I After 14900 nodes, 5923 on tree, 139123.24 best solution, best possible 131456.77 (221.25 seconds)\n", + "Cbc0010I After 15000 nodes, 5971 on tree, 139123.24 best solution, best possible 131468.02 (222.87 seconds)\n", + "Cbc0010I After 15100 nodes, 6021 on tree, 139123.24 best solution, best possible 131478 (225.58 seconds)\n", + "Cbc0010I After 15200 nodes, 6067 on tree, 139123.24 best solution, best possible 131489.55 (227.24 seconds)\n", + "Cbc0010I After 15300 nodes, 6116 on tree, 139123.24 best solution, best possible 131501.06 (228.68 seconds)\n", + "Cbc0010I After 15400 nodes, 6165 on tree, 139123.24 best solution, best possible 131508.74 (230.27 seconds)\n", + "Cbc0010I After 15500 nodes, 6215 on tree, 139123.24 best solution, best possible 131516.54 (231.96 seconds)\n", + "Cbc0010I After 15600 nodes, 6265 on tree, 139123.24 best solution, best possible 131528.73 (233.74 seconds)\n", + "Cbc0010I After 15700 nodes, 6314 on tree, 139123.24 best solution, best possible 131537.41 (235.48 seconds)\n", + "Cbc0010I After 15800 nodes, 6363 on tree, 139123.24 best solution, best possible 131545.72 (237.26 seconds)\n", + "Cbc0010I After 15900 nodes, 6411 on tree, 139123.24 best solution, best possible 131553.8 (239.79 seconds)\n", + "Cbc0010I After 16000 nodes, 6461 on tree, 139123.24 best solution, best possible 131565.93 (241.54 seconds)\n", + "Cbc0010I After 16100 nodes, 6511 on tree, 139123.24 best solution, best possible 131571.72 (243.21 seconds)\n", + "Cbc0010I After 16200 nodes, 6560 on tree, 139123.24 best solution, best possible 131578.23 (245.02 seconds)\n", + "Cbc0010I After 16300 nodes, 6609 on tree, 139123.24 best solution, best possible 131585.48 (246.53 seconds)\n", + "Cbc0010I After 16400 nodes, 6659 on tree, 139123.24 best solution, best possible 131591.97 (248.42 seconds)\n", + "Cbc0010I After 16500 nodes, 6709 on tree, 139123.24 best solution, best possible 131599.12 (250.77 seconds)\n", + "Cbc0010I After 16600 nodes, 6759 on tree, 139123.24 best solution, best possible 131604.69 (253.12 seconds)\n", + "Cbc0010I After 16700 nodes, 6808 on tree, 139123.24 best solution, best possible 131612.33 (254.65 seconds)\n", + "Cbc0010I After 16800 nodes, 6858 on tree, 139123.24 best solution, best possible 131619.92 (256.59 seconds)\n", + "Cbc0010I After 16900 nodes, 6908 on tree, 139123.24 best solution, best possible 131626.31 (258.32 seconds)\n", + "Cbc0010I After 17000 nodes, 6958 on tree, 139123.24 best solution, best possible 131634.03 (259.96 seconds)\n", + "Cbc0010I After 17100 nodes, 7006 on tree, 139123.24 best solution, best possible 131634.69 (261.34 seconds)\n", + "Cbc0010I After 17200 nodes, 7050 on tree, 139123.24 best solution, best possible 131634.69 (263.65 seconds)\n", + "Cbc0010I After 17300 nodes, 7093 on tree, 139123.24 best solution, best possible 131634.69 (265.60 seconds)\n", + "Cbc0010I After 17400 nodes, 7139 on tree, 139123.24 best solution, best possible 131634.69 (266.98 seconds)\n", + "Cbc0010I After 17500 nodes, 7185 on tree, 139123.24 best solution, best possible 131634.69 (268.34 seconds)\n", + "Cbc0010I After 17600 nodes, 7236 on tree, 139123.24 best solution, best possible 131634.69 (269.80 seconds)\n", + "Cbc0010I After 17700 nodes, 7283 on tree, 139123.24 best solution, best possible 131634.69 (271.17 seconds)\n", + "Cbc0010I After 17800 nodes, 7330 on tree, 139123.24 best solution, best possible 131634.69 (272.37 seconds)\n", + "Cbc0010I After 17900 nodes, 7378 on tree, 139123.24 best solution, best possible 131634.69 (273.51 seconds)\n", + "Cbc0010I After 18000 nodes, 7425 on tree, 139123.24 best solution, best possible 131634.69 (274.79 seconds)\n", + "Cbc0010I After 18100 nodes, 7474 on tree, 139123.24 best solution, best possible 131642.16 (277.76 seconds)\n", + "Cbc0010I After 18200 nodes, 7522 on tree, 139123.24 best solution, best possible 131649.76 (279.45 seconds)\n", + "Cbc0010I After 18300 nodes, 7572 on tree, 139123.24 best solution, best possible 131656.91 (281.19 seconds)\n", + "Cbc0010I After 18400 nodes, 7621 on tree, 139123.24 best solution, best possible 131663.6 (282.98 seconds)\n", + "Cbc0010I After 18500 nodes, 7670 on tree, 139123.24 best solution, best possible 131670.18 (284.41 seconds)\n", + "Cbc0010I After 18600 nodes, 7719 on tree, 139123.24 best solution, best possible 131676.78 (286.07 seconds)\n", + "Cbc0010I After 18700 nodes, 7766 on tree, 139123.24 best solution, best possible 131683.67 (287.66 seconds)\n", + "Cbc0010I After 18800 nodes, 7813 on tree, 139123.24 best solution, best possible 131689.62 (290.17 seconds)\n", + "Cbc0010I After 18900 nodes, 7861 on tree, 139123.24 best solution, best possible 131696.21 (292.24 seconds)\n", + "Cbc0010I After 19000 nodes, 7911 on tree, 139123.24 best solution, best possible 131702.32 (293.91 seconds)\n", + "Cbc0010I After 19100 nodes, 7960 on tree, 139123.24 best solution, best possible 131707.48 (295.79 seconds)\n", + "Cbc0010I After 19200 nodes, 8010 on tree, 139123.24 best solution, best possible 131713.83 (297.54 seconds)\n", + "Cbc0010I After 19300 nodes, 8059 on tree, 139123.24 best solution, best possible 131720.38 (298.94 seconds)\n", + "Cbc0010I After 19400 nodes, 8107 on tree, 139123.24 best solution, best possible 131726.8 (300.95 seconds)\n", + "Cbc0010I After 19500 nodes, 8155 on tree, 139123.24 best solution, best possible 131731.49 (303.89 seconds)\n", + "Cbc0010I After 19600 nodes, 8202 on tree, 139123.24 best solution, best possible 131736.86 (305.15 seconds)\n", + "Cbc0010I After 19700 nodes, 8250 on tree, 139123.24 best solution, best possible 131743.45 (306.57 seconds)\n", + "Cbc0010I After 19800 nodes, 8300 on tree, 139123.24 best solution, best possible 131749.07 (308.17 seconds)\n", + "Cbc0010I After 19900 nodes, 8347 on tree, 139123.24 best solution, best possible 131753.94 (309.67 seconds)\n", + "Cbc0010I After 20000 nodes, 8397 on tree, 139123.24 best solution, best possible 131759.78 (311.38 seconds)\n", + "Cbc0010I After 20100 nodes, 8445 on tree, 139123.24 best solution, best possible 131765.91 (313.02 seconds)\n", + "Cbc0010I After 20200 nodes, 8494 on tree, 139123.24 best solution, best possible 131770.27 (315.43 seconds)\n", + "Cbc0010I After 20300 nodes, 8543 on tree, 139123.24 best solution, best possible 131776.37 (317.32 seconds)\n", + "Cbc0010I After 20400 nodes, 8591 on tree, 139123.24 best solution, best possible 131781.25 (318.82 seconds)\n", + "Cbc0010I After 20500 nodes, 8639 on tree, 139123.24 best solution, best possible 131787.02 (320.09 seconds)\n", + "Cbc0010I After 20600 nodes, 8687 on tree, 139123.24 best solution, best possible 131791.96 (321.58 seconds)\n", + "Cbc0010I After 20700 nodes, 8735 on tree, 139123.24 best solution, best possible 131795.59 (323.06 seconds)\n", + "Cbc0010I After 20800 nodes, 8782 on tree, 139123.24 best solution, best possible 131801.46 (324.76 seconds)\n", + "Cbc0010I After 20900 nodes, 8832 on tree, 139123.24 best solution, best possible 131806.26 (326.40 seconds)\n", + "Cbc0010I After 21000 nodes, 8882 on tree, 139123.24 best solution, best possible 131810.27 (328.63 seconds)\n", + "Cbc0010I After 21100 nodes, 8931 on tree, 139123.24 best solution, best possible 131810.27 (330.26 seconds)\n", + "Cbc0010I After 21200 nodes, 8980 on tree, 139123.24 best solution, best possible 131810.27 (331.45 seconds)\n", + "Cbc0010I After 21300 nodes, 9030 on tree, 139123.24 best solution, best possible 131810.27 (332.80 seconds)\n", + "Cbc0010I After 21400 nodes, 9078 on tree, 139123.24 best solution, best possible 131810.27 (334.05 seconds)\n", + "Cbc0010I After 21500 nodes, 9115 on tree, 139123.24 best solution, best possible 131810.27 (335.43 seconds)\n", + "Cbc0010I After 21600 nodes, 9159 on tree, 139123.24 best solution, best possible 131810.27 (336.70 seconds)\n", + "Cbc0010I After 21700 nodes, 9206 on tree, 139123.24 best solution, best possible 131810.27 (338.00 seconds)\n", + "Cbc0010I After 21800 nodes, 9253 on tree, 139123.24 best solution, best possible 131810.27 (339.11 seconds)\n", + "Cbc0010I After 21900 nodes, 9299 on tree, 139123.24 best solution, best possible 131810.27 (340.68 seconds)\n", + "Cbc0010I After 22000 nodes, 9349 on tree, 139123.24 best solution, best possible 131810.27 (342.76 seconds)\n", + "Cbc0010I After 22100 nodes, 9397 on tree, 139123.24 best solution, best possible 131815.81 (344.64 seconds)\n", + "Cbc0010I After 22200 nodes, 9445 on tree, 139123.24 best solution, best possible 131820.07 (346.13 seconds)\n", + "Cbc0010I After 22300 nodes, 9492 on tree, 139123.24 best solution, best possible 131823.82 (347.52 seconds)\n", + "Cbc0010I After 22400 nodes, 9539 on tree, 139123.24 best solution, best possible 131828.27 (349.23 seconds)\n", + "Cbc0010I After 22500 nodes, 9587 on tree, 139123.24 best solution, best possible 131833.08 (350.73 seconds)\n", + "Cbc0010I After 22600 nodes, 9633 on tree, 139123.24 best solution, best possible 131838.72 (352.13 seconds)\n", + "Cbc0010I After 22700 nodes, 9682 on tree, 139123.24 best solution, best possible 131842.27 (354.15 seconds)\n", + "Cbc0010I After 22800 nodes, 9732 on tree, 139123.24 best solution, best possible 131845.96 (356.41 seconds)\n", + "Cbc0010I After 22900 nodes, 9782 on tree, 139123.24 best solution, best possible 131849.54 (357.84 seconds)\n", + "Cbc0010I After 23000 nodes, 9831 on tree, 139123.24 best solution, best possible 131852.66 (359.69 seconds)\n", + "Cbc0010I After 23100 nodes, 9879 on tree, 139123.24 best solution, best possible 131856.73 (361.18 seconds)\n", + "Cbc0010I After 23200 nodes, 9929 on tree, 139123.24 best solution, best possible 131861.66 (362.73 seconds)\n", + "Cbc0010I After 23300 nodes, 9978 on tree, 139123.24 best solution, best possible 131866.09 (364.30 seconds)\n", + "Cbc0010I After 23400 nodes, 10027 on tree, 139123.24 best solution, best possible 131869.54 (366.29 seconds)\n", + "Cbc0010I After 23500 nodes, 10075 on tree, 139123.24 best solution, best possible 131873.91 (368.76 seconds)\n", + "Cbc0010I After 23600 nodes, 10121 on tree, 139123.24 best solution, best possible 131877.65 (370.23 seconds)\n", + "Cbc0010I After 23700 nodes, 10169 on tree, 139123.24 best solution, best possible 131880.85 (371.81 seconds)\n", + "Cbc0010I After 23800 nodes, 10219 on tree, 139123.24 best solution, best possible 131883.99 (373.52 seconds)\n", + "Cbc0010I After 23900 nodes, 10268 on tree, 139123.24 best solution, best possible 131888.56 (375.14 seconds)\n", + "Cbc0010I After 24000 nodes, 10317 on tree, 139123.24 best solution, best possible 131893.7 (376.84 seconds)\n", + "Cbc0010I After 24100 nodes, 10307 on tree, 139123.24 best solution, best possible 131893.7 (377.18 seconds)\n", + "Cbc0010I After 24200 nodes, 10308 on tree, 139123.24 best solution, best possible 131893.7 (377.50 seconds)\n", + "Cbc0010I After 24300 nodes, 10318 on tree, 139123.24 best solution, best possible 131893.7 (378.08 seconds)\n", + "Cbc0010I After 24400 nodes, 10318 on tree, 139123.24 best solution, best possible 131893.7 (378.70 seconds)\n", + "Cbc0010I After 24500 nodes, 10313 on tree, 139123.24 best solution, best possible 131893.7 (379.57 seconds)\n", + "Cbc0010I After 24600 nodes, 10310 on tree, 139123.24 best solution, best possible 131893.7 (380.37 seconds)\n", + "Cbc0010I After 24700 nodes, 10309 on tree, 139123.24 best solution, best possible 131893.7 (381.23 seconds)\n", + "Cbc0010I After 24800 nodes, 10313 on tree, 139123.24 best solution, best possible 131893.7 (381.77 seconds)\n", + "Cbc0010I After 24900 nodes, 10316 on tree, 139123.24 best solution, best possible 131893.7 (382.45 seconds)\n", + "Cbc0010I After 25000 nodes, 10325 on tree, 139123.24 best solution, best possible 131893.7 (383.19 seconds)\n", + "Cbc0010I After 25100 nodes, 10375 on tree, 139123.24 best solution, best possible 131896.73 (384.83 seconds)\n", + "Cbc0010I After 25200 nodes, 10422 on tree, 139123.24 best solution, best possible 131901.33 (386.25 seconds)\n", + "Cbc0010I After 25300 nodes, 10471 on tree, 139123.24 best solution, best possible 131905.93 (387.87 seconds)\n", + "Cbc0010I After 25400 nodes, 10520 on tree, 139123.24 best solution, best possible 131910.34 (389.43 seconds)\n", + "Cbc0010I After 25500 nodes, 10566 on tree, 139123.24 best solution, best possible 131914.11 (390.87 seconds)\n", + "Cbc0010I After 25600 nodes, 10615 on tree, 139123.24 best solution, best possible 131918.3 (393.29 seconds)\n", + "Cbc0010I After 25700 nodes, 10663 on tree, 139123.24 best solution, best possible 131921.84 (394.94 seconds)\n", + "Cbc0010I After 25800 nodes, 10713 on tree, 139123.24 best solution, best possible 131925.24 (396.63 seconds)\n", + "Cbc0010I After 25900 nodes, 10762 on tree, 139123.24 best solution, best possible 131929.07 (398.20 seconds)\n", + "Cbc0010I After 26000 nodes, 10812 on tree, 139123.24 best solution, best possible 131933.38 (399.95 seconds)\n", + "Cbc0010I After 26100 nodes, 10861 on tree, 139123.24 best solution, best possible 131933.5 (401.18 seconds)\n", + "Cbc0010I After 26200 nodes, 10911 on tree, 139123.24 best solution, best possible 131933.5 (402.42 seconds)\n", + "Cbc0010I After 26300 nodes, 10958 on tree, 139123.24 best solution, best possible 131933.5 (403.68 seconds)\n", + "Cbc0010I After 26400 nodes, 11008 on tree, 139123.24 best solution, best possible 131933.5 (405.79 seconds)\n", + "Cbc0010I After 26500 nodes, 11050 on tree, 139123.24 best solution, best possible 131933.5 (407.45 seconds)\n", + "Cbc0010I After 26600 nodes, 11096 on tree, 139123.24 best solution, best possible 131933.5 (408.77 seconds)\n", + "Cbc0010I After 26700 nodes, 11142 on tree, 139123.24 best solution, best possible 131933.5 (410.18 seconds)\n", + "Cbc0010I After 26800 nodes, 11190 on tree, 139123.24 best solution, best possible 131933.5 (411.08 seconds)\n", + "Cbc0010I After 26900 nodes, 11234 on tree, 139123.24 best solution, best possible 131933.5 (412.17 seconds)\n", + "Cbc0010I After 27000 nodes, 11284 on tree, 139123.24 best solution, best possible 131933.5 (413.20 seconds)\n", + "Cbc0010I After 27100 nodes, 11330 on tree, 139123.24 best solution, best possible 131933.5 (414.31 seconds)\n", + "Cbc0010I After 27200 nodes, 11376 on tree, 139123.24 best solution, best possible 131933.5 (415.72 seconds)\n", + "Cbc0010I After 27300 nodes, 11424 on tree, 139123.24 best solution, best possible 131933.5 (417.58 seconds)\n", + "Cbc0010I After 27400 nodes, 11473 on tree, 139123.24 best solution, best possible 131933.5 (419.87 seconds)\n", + "Cbc0010I After 27500 nodes, 11517 on tree, 139123.24 best solution, best possible 131933.5 (421.19 seconds)\n", + "Cbc0010I After 27600 nodes, 11569 on tree, 139123.24 best solution, best possible 131933.5 (422.62 seconds)\n", + "Cbc0010I After 27700 nodes, 11616 on tree, 139123.24 best solution, best possible 131933.5 (423.91 seconds)\n", + "Cbc0010I After 27800 nodes, 11665 on tree, 139123.24 best solution, best possible 131933.5 (425.31 seconds)\n", + "Cbc0010I After 27900 nodes, 11715 on tree, 139123.24 best solution, best possible 131933.5 (426.33 seconds)\n", + "Cbc0010I After 28000 nodes, 11765 on tree, 139123.24 best solution, best possible 131933.5 (427.40 seconds)\n", + "Cbc0010I After 28100 nodes, 11787 on tree, 139123.24 best solution, best possible 131933.5 (428.02 seconds)\n", + "Cbc0010I After 28200 nodes, 11784 on tree, 139123.24 best solution, best possible 131933.5 (428.34 seconds)\n", + "Cbc0010I After 28300 nodes, 11786 on tree, 139123.24 best solution, best possible 131933.5 (428.89 seconds)\n", + "Cbc0010I After 28400 nodes, 11793 on tree, 139123.24 best solution, best possible 131933.5 (429.49 seconds)\n", + "Cbc0010I After 28500 nodes, 11792 on tree, 139123.24 best solution, best possible 131933.5 (430.19 seconds)\n", + "Cbc0010I After 28600 nodes, 11790 on tree, 139123.24 best solution, best possible 131933.5 (430.86 seconds)\n", + "Cbc0010I After 28700 nodes, 11793 on tree, 139123.24 best solution, best possible 131933.5 (431.91 seconds)\n", + "Cbc0010I After 28800 nodes, 11801 on tree, 139123.24 best solution, best possible 131933.5 (432.71 seconds)\n", + "Cbc0010I After 28900 nodes, 11800 on tree, 139123.24 best solution, best possible 131933.5 (433.21 seconds)\n", + "Cbc0010I After 29000 nodes, 11808 on tree, 139123.24 best solution, best possible 131933.5 (433.80 seconds)\n", + "Cbc0010I After 29100 nodes, 11855 on tree, 139123.24 best solution, best possible 131937.79 (435.37 seconds)\n", + "Cbc0010I After 29200 nodes, 11902 on tree, 139123.24 best solution, best possible 131941.46 (436.89 seconds)\n", + "Cbc0010I After 29300 nodes, 11951 on tree, 139123.24 best solution, best possible 131945.51 (438.54 seconds)\n", + "Cbc0010I After 29400 nodes, 12000 on tree, 139123.24 best solution, best possible 131949.69 (439.80 seconds)\n", + "Cbc0010I After 29500 nodes, 12049 on tree, 139123.24 best solution, best possible 131953.52 (441.07 seconds)\n", + "Cbc0010I After 29600 nodes, 12098 on tree, 139123.24 best solution, best possible 131957.38 (442.43 seconds)\n", + "Cbc0010I After 29700 nodes, 12146 on tree, 139123.24 best solution, best possible 131960.72 (444.93 seconds)\n", + "Cbc0010I After 29800 nodes, 12195 on tree, 139123.24 best solution, best possible 131963.63 (446.42 seconds)\n", + "Cbc0010I After 29900 nodes, 12243 on tree, 139123.24 best solution, best possible 131967.1 (448.12 seconds)\n", + "Cbc0010I After 30000 nodes, 12292 on tree, 139123.24 best solution, best possible 131970.51 (449.82 seconds)\n", + "Cbc0010I After 30100 nodes, 12340 on tree, 139123.24 best solution, best possible 131970.53 (451.11 seconds)\n", + "Cbc0010I After 30200 nodes, 12385 on tree, 139123.24 best solution, best possible 131970.53 (452.51 seconds)\n", + "Cbc0010I After 30300 nodes, 12433 on tree, 139123.24 best solution, best possible 131970.53 (453.59 seconds)\n", + "Cbc0010I After 30400 nodes, 12481 on tree, 139123.24 best solution, best possible 131970.53 (454.81 seconds)\n", + "Cbc0010I After 30500 nodes, 12529 on tree, 139123.24 best solution, best possible 131970.53 (456.57 seconds)\n", + "Cbc0010I After 30600 nodes, 12575 on tree, 139123.24 best solution, best possible 131970.53 (458.55 seconds)\n", + "Cbc0010I After 30700 nodes, 12624 on tree, 139123.24 best solution, best possible 131970.53 (459.53 seconds)\n", + "Cbc0010I After 30800 nodes, 12669 on tree, 139123.24 best solution, best possible 131970.53 (460.57 seconds)\n", + "Cbc0010I After 30900 nodes, 12716 on tree, 139123.24 best solution, best possible 131970.53 (461.72 seconds)\n", + "Cbc0010I After 31000 nodes, 12762 on tree, 139123.24 best solution, best possible 131970.53 (462.79 seconds)\n", + "Cbc0010I After 31100 nodes, 12811 on tree, 139123.24 best solution, best possible 131970.53 (464.30 seconds)\n", + "Cbc0010I After 31200 nodes, 12861 on tree, 139123.24 best solution, best possible 131970.53 (465.51 seconds)\n", + "Cbc0010I After 31300 nodes, 12911 on tree, 139123.24 best solution, best possible 131970.53 (466.69 seconds)\n", + "Cbc0010I After 31400 nodes, 12959 on tree, 139123.24 best solution, best possible 131970.53 (467.94 seconds)\n", + "Cbc0010I After 31500 nodes, 13006 on tree, 139123.24 best solution, best possible 131970.53 (469.74 seconds)\n", + "Cbc0010I After 31600 nodes, 13051 on tree, 139123.24 best solution, best possible 131970.53 (471.46 seconds)\n", + "Cbc0010I After 31700 nodes, 13097 on tree, 139123.24 best solution, best possible 131970.53 (472.62 seconds)\n", + "Cbc0010I After 31800 nodes, 13143 on tree, 139123.24 best solution, best possible 131970.53 (473.72 seconds)\n", + "Cbc0010I After 31900 nodes, 13190 on tree, 139123.24 best solution, best possible 131970.53 (474.80 seconds)\n", + "Cbc0010I After 32000 nodes, 13237 on tree, 139123.24 best solution, best possible 131970.53 (475.91 seconds)\n", + "Cbc0010I After 32100 nodes, 13252 on tree, 139123.24 best solution, best possible 131970.53 (476.45 seconds)\n", + "Cbc0010I After 32200 nodes, 13261 on tree, 139123.24 best solution, best possible 131970.53 (476.97 seconds)\n", + "Cbc0010I After 32300 nodes, 13260 on tree, 139123.24 best solution, best possible 131970.53 (477.38 seconds)\n", + "Cbc0012I Integer solution of 138710.15 found by heuristic after 1740711 iterations and 32338 nodes (477.51 seconds)\n", + "Cbc0010I After 32400 nodes, 13088 on tree, 138710.15 best solution, best possible 131970.53 (478.15 seconds)\n", + "Cbc0010I After 32500 nodes, 13135 on tree, 138710.15 best solution, best possible 131970.53 (479.47 seconds)\n", + "Cbc0010I After 32600 nodes, 13182 on tree, 138710.15 best solution, best possible 131970.53 (480.74 seconds)\n", + "Cbc0010I After 32700 nodes, 13230 on tree, 138710.15 best solution, best possible 131970.53 (482.44 seconds)\n", + "Cbc0010I After 32800 nodes, 13279 on tree, 138710.15 best solution, best possible 131970.53 (484.05 seconds)\n", + "Cbc0010I After 32900 nodes, 13327 on tree, 138710.15 best solution, best possible 131970.53 (485.18 seconds)\n", + "Cbc0010I After 33000 nodes, 13378 on tree, 138710.15 best solution, best possible 131970.53 (486.48 seconds)\n", + "Cbc0010I After 33100 nodes, 13429 on tree, 138710.15 best solution, best possible 131974.29 (488.16 seconds)\n", + "Cbc0010I After 33200 nodes, 13477 on tree, 138710.15 best solution, best possible 131977.29 (489.95 seconds)\n", + "Cbc0010I After 33300 nodes, 13523 on tree, 138710.15 best solution, best possible 131981.43 (491.40 seconds)\n", + "Cbc0010I After 33400 nodes, 13571 on tree, 138710.15 best solution, best possible 131984.78 (492.92 seconds)\n", + "Cbc0010I After 33500 nodes, 13619 on tree, 138710.15 best solution, best possible 131988.63 (494.44 seconds)\n", + "Cbc0010I After 33600 nodes, 13668 on tree, 138710.15 best solution, best possible 131992.1 (496.99 seconds)\n", + "Cbc0010I After 33700 nodes, 13715 on tree, 138710.15 best solution, best possible 131995 (498.35 seconds)\n", + "Cbc0010I After 33800 nodes, 13764 on tree, 138710.15 best solution, best possible 131998.34 (500.01 seconds)\n", + "Cbc0010I After 33900 nodes, 13814 on tree, 138710.15 best solution, best possible 132000.86 (501.72 seconds)\n", + "Cbc0010I After 34000 nodes, 13860 on tree, 138710.15 best solution, best possible 132004.23 (503.11 seconds)\n", + "Cbc0010I After 34100 nodes, 13909 on tree, 138710.15 best solution, best possible 132004.42 (504.51 seconds)\n", + "Cbc0010I After 34200 nodes, 13959 on tree, 138710.15 best solution, best possible 132004.42 (506.05 seconds)\n", + "Cbc0010I After 34300 nodes, 14007 on tree, 138710.15 best solution, best possible 132004.42 (507.49 seconds)\n", + "Cbc0010I After 34400 nodes, 14053 on tree, 138710.15 best solution, best possible 132004.42 (509.90 seconds)\n", + "Cbc0010I After 34500 nodes, 14103 on tree, 138710.15 best solution, best possible 132004.42 (511.44 seconds)\n", + "Cbc0010I After 34600 nodes, 14151 on tree, 138710.15 best solution, best possible 132004.42 (512.81 seconds)\n", + "Cbc0010I After 34700 nodes, 14196 on tree, 138710.15 best solution, best possible 132004.42 (514.17 seconds)\n", + "Cbc0010I After 34800 nodes, 14244 on tree, 138710.15 best solution, best possible 132004.42 (515.44 seconds)\n", + "Cbc0010I After 34900 nodes, 14291 on tree, 138710.15 best solution, best possible 132004.42 (516.66 seconds)\n", + "Cbc0010I After 35000 nodes, 14337 on tree, 138710.15 best solution, best possible 132004.42 (517.88 seconds)\n", + "Cbc0010I After 35100 nodes, 14386 on tree, 138710.15 best solution, best possible 132004.42 (519.13 seconds)\n", + "Cbc0010I After 35200 nodes, 14432 on tree, 138710.15 best solution, best possible 132004.42 (520.98 seconds)\n", + "Cbc0010I After 35300 nodes, 14480 on tree, 138710.15 best solution, best possible 132004.42 (522.65 seconds)\n", + "Cbc0010I After 35400 nodes, 14527 on tree, 138710.15 best solution, best possible 132004.42 (524.07 seconds)\n", + "Cbc0010I After 35500 nodes, 14578 on tree, 138710.15 best solution, best possible 132004.42 (525.15 seconds)\n", + "Cbc0010I After 35600 nodes, 14629 on tree, 138710.15 best solution, best possible 132004.42 (526.38 seconds)\n", + "Cbc0010I After 35700 nodes, 14675 on tree, 138710.15 best solution, best possible 132004.42 (527.55 seconds)\n", + "Cbc0010I After 35800 nodes, 14723 on tree, 138710.15 best solution, best possible 132004.42 (528.85 seconds)\n", + "Cbc0010I After 35900 nodes, 14767 on tree, 138710.15 best solution, best possible 132004.42 (530.03 seconds)\n", + "Cbc0010I After 36000 nodes, 14818 on tree, 138710.15 best solution, best possible 132004.42 (531.07 seconds)\n", + "Cbc0010I After 36100 nodes, 14850 on tree, 138710.15 best solution, best possible 132004.42 (531.59 seconds)\n", + "Cbc0010I After 36200 nodes, 14871 on tree, 138710.15 best solution, best possible 132004.42 (532.17 seconds)\n", + "Cbc0010I After 36300 nodes, 14887 on tree, 138710.15 best solution, best possible 132004.42 (532.85 seconds)\n", + "Cbc0010I After 36400 nodes, 14919 on tree, 138710.15 best solution, best possible 132004.42 (533.98 seconds)\n", + "Cbc0010I After 36500 nodes, 14943 on tree, 138710.15 best solution, best possible 132004.42 (535.04 seconds)\n", + "Cbc0010I After 36600 nodes, 14963 on tree, 138710.15 best solution, best possible 132004.42 (535.81 seconds)\n", + "Cbc0010I After 36700 nodes, 14982 on tree, 138710.15 best solution, best possible 132004.42 (536.38 seconds)\n", + "Cbc0010I After 36800 nodes, 14994 on tree, 138710.15 best solution, best possible 132004.42 (536.88 seconds)\n", + "Cbc0010I After 36900 nodes, 15012 on tree, 138710.15 best solution, best possible 132004.42 (537.44 seconds)\n", + "Cbc0010I After 37000 nodes, 15021 on tree, 138710.15 best solution, best possible 132004.42 (537.93 seconds)\n", + "Cbc0010I After 37100 nodes, 15069 on tree, 138710.15 best solution, best possible 132007.68 (539.56 seconds)\n", + "Cbc0010I After 37200 nodes, 15116 on tree, 138710.15 best solution, best possible 132010.59 (540.98 seconds)\n", + "Cbc0010I After 37300 nodes, 15166 on tree, 138710.15 best solution, best possible 132014.57 (542.49 seconds)\n", + "Cbc0010I After 37400 nodes, 15215 on tree, 138710.15 best solution, best possible 132017.48 (544.21 seconds)\n", + "Cbc0010I After 37500 nodes, 15262 on tree, 138710.15 best solution, best possible 132020.99 (546.26 seconds)\n", + "Cbc0010I After 37600 nodes, 15310 on tree, 138710.15 best solution, best possible 132024.46 (548.88 seconds)\n", + "Cbc0010I After 37700 nodes, 15357 on tree, 138710.15 best solution, best possible 132027.47 (550.81 seconds)\n", + "Cbc0010I After 37800 nodes, 15404 on tree, 138710.15 best solution, best possible 132030.79 (553.21 seconds)\n", + "Cbc0010I After 37900 nodes, 15452 on tree, 138710.15 best solution, best possible 132034.81 (554.62 seconds)\n", + "Cbc0010I After 38000 nodes, 15502 on tree, 138710.15 best solution, best possible 132038.89 (556.29 seconds)\n", + "Cbc0010I After 38100 nodes, 15548 on tree, 138710.15 best solution, best possible 132038.94 (557.51 seconds)\n", + "Cbc0010I After 38200 nodes, 15599 on tree, 138710.15 best solution, best possible 132038.94 (558.93 seconds)\n", + "Cbc0010I After 38300 nodes, 15648 on tree, 138710.15 best solution, best possible 132038.94 (560.59 seconds)\n", + "Cbc0010I After 38400 nodes, 15696 on tree, 138710.15 best solution, best possible 132038.94 (561.68 seconds)\n", + "Cbc0010I After 38500 nodes, 15742 on tree, 138710.15 best solution, best possible 132038.94 (562.71 seconds)\n", + "Cbc0010I After 38600 nodes, 15792 on tree, 138710.15 best solution, best possible 132038.94 (564.03 seconds)\n", + "Cbc0010I After 38700 nodes, 15839 on tree, 138710.15 best solution, best possible 132038.94 (565.17 seconds)\n", + "Cbc0010I After 38800 nodes, 15887 on tree, 138710.15 best solution, best possible 132038.94 (566.44 seconds)\n", + "Cbc0010I After 38900 nodes, 15936 on tree, 138710.15 best solution, best possible 132038.94 (567.62 seconds)\n", + "Cbc0010I After 39000 nodes, 15984 on tree, 138710.15 best solution, best possible 132038.94 (568.70 seconds)\n", + "Cbc0010I After 39100 nodes, 16035 on tree, 138710.15 best solution, best possible 132038.94 (569.84 seconds)\n", + "Cbc0010I After 39200 nodes, 16083 on tree, 138710.15 best solution, best possible 132038.94 (571.06 seconds)\n", + "Cbc0010I After 39300 nodes, 16132 on tree, 138710.15 best solution, best possible 132038.94 (572.90 seconds)\n", + "Cbc0010I After 39400 nodes, 16182 on tree, 138710.15 best solution, best possible 132038.94 (574.30 seconds)\n", + "Cbc0010I After 39500 nodes, 16228 on tree, 138710.15 best solution, best possible 132038.94 (575.44 seconds)\n", + "Cbc0010I After 39600 nodes, 16277 on tree, 138710.15 best solution, best possible 132038.94 (576.74 seconds)\n", + "Cbc0010I After 39700 nodes, 16323 on tree, 138710.15 best solution, best possible 132038.94 (578.00 seconds)\n", + "Cbc0010I After 39800 nodes, 16367 on tree, 138710.15 best solution, best possible 132038.94 (578.89 seconds)\n", + "Cbc0010I After 39900 nodes, 16407 on tree, 138710.15 best solution, best possible 132038.94 (579.85 seconds)\n", + "Cbc0010I After 40000 nodes, 16455 on tree, 138710.15 best solution, best possible 132038.94 (580.88 seconds)\n", + "Cbc0010I After 40100 nodes, 16470 on tree, 138710.15 best solution, best possible 132038.94 (581.30 seconds)\n", + "Cbc0012I Integer solution of 137528.52 found by heuristic after 2179663 iterations and 40160 nodes (581.55 seconds)\n", + "Cbc0010I After 40200 nodes, 15560 on tree, 137528.52 best solution, best possible 132038.94 (582.16 seconds)\n", + "Cbc0010I After 40300 nodes, 15608 on tree, 137528.52 best solution, best possible 132038.94 (583.42 seconds)\n", + "Cbc0010I After 40400 nodes, 15656 on tree, 137528.52 best solution, best possible 132038.94 (585.48 seconds)\n", + "Cbc0010I After 40500 nodes, 15703 on tree, 137528.52 best solution, best possible 132038.94 (587.36 seconds)\n", + "Cbc0010I After 40600 nodes, 15754 on tree, 137528.52 best solution, best possible 132038.94 (588.71 seconds)\n", + "Cbc0010I After 40700 nodes, 15802 on tree, 137528.52 best solution, best possible 132038.94 (589.96 seconds)\n", + "Cbc0010I After 40800 nodes, 15849 on tree, 137528.52 best solution, best possible 132038.94 (591.32 seconds)\n", + "Cbc0010I After 40900 nodes, 15895 on tree, 137528.52 best solution, best possible 132038.94 (592.77 seconds)\n", + "Cbc0010I After 41000 nodes, 15944 on tree, 137528.52 best solution, best possible 132038.94 (594.24 seconds)\n", + "Cbc0010I After 41100 nodes, 15992 on tree, 137528.52 best solution, best possible 132042.39 (595.62 seconds)\n", + "Cbc0010I After 41200 nodes, 16038 on tree, 137528.52 best solution, best possible 132045.7 (597.13 seconds)\n", + "Cbc0010I After 41300 nodes, 16087 on tree, 137528.52 best solution, best possible 132048.84 (599.72 seconds)\n", + "Cbc0010I After 41400 nodes, 16136 on tree, 137528.52 best solution, best possible 132053.61 (601.27 seconds)\n", + "Cbc0010I After 41500 nodes, 16184 on tree, 137528.52 best solution, best possible 132056.97 (602.91 seconds)\n", + "Cbc0010I After 41600 nodes, 16234 on tree, 137528.52 best solution, best possible 132059.38 (604.37 seconds)\n", + "Cbc0010I After 41700 nodes, 16282 on tree, 137528.52 best solution, best possible 132062.12 (605.51 seconds)\n", + "Cbc0010I After 41800 nodes, 16329 on tree, 137528.52 best solution, best possible 132065.07 (607.00 seconds)\n", + "Cbc0010I After 41900 nodes, 16376 on tree, 137528.52 best solution, best possible 132068.74 (608.22 seconds)\n", + "Cbc0010I After 42000 nodes, 16424 on tree, 137528.52 best solution, best possible 132071.83 (609.77 seconds)\n", + "Cbc0010I After 42100 nodes, 16473 on tree, 137528.52 best solution, best possible 132072.28 (612.14 seconds)\n", + "Cbc0010I After 42200 nodes, 16520 on tree, 137528.52 best solution, best possible 132072.28 (613.61 seconds)\n", + "Cbc0010I After 42300 nodes, 16569 on tree, 137528.52 best solution, best possible 132072.28 (615.08 seconds)\n", + "Cbc0010I After 42400 nodes, 16617 on tree, 137528.52 best solution, best possible 132072.28 (616.16 seconds)\n", + "Cbc0010I After 42500 nodes, 16664 on tree, 137528.52 best solution, best possible 132072.28 (617.37 seconds)\n", + "Cbc0010I After 42600 nodes, 16714 on tree, 137528.52 best solution, best possible 132072.28 (618.46 seconds)\n", + "Cbc0010I After 42700 nodes, 16761 on tree, 137528.52 best solution, best possible 132072.28 (619.84 seconds)\n", + "Cbc0010I After 42800 nodes, 16806 on tree, 137528.52 best solution, best possible 132072.28 (621.06 seconds)\n", + "Cbc0010I After 42900 nodes, 16852 on tree, 137528.52 best solution, best possible 132072.28 (622.12 seconds)\n", + "Cbc0010I After 43000 nodes, 16899 on tree, 137528.52 best solution, best possible 132072.28 (624.18 seconds)\n", + "Cbc0010I After 43100 nodes, 16946 on tree, 137528.52 best solution, best possible 132072.28 (625.65 seconds)\n", + "Cbc0010I After 43200 nodes, 16994 on tree, 137528.52 best solution, best possible 132072.28 (627.02 seconds)\n", + "Cbc0010I After 43300 nodes, 17044 on tree, 137528.52 best solution, best possible 132072.28 (628.12 seconds)\n", + "Cbc0010I After 43400 nodes, 17092 on tree, 137528.52 best solution, best possible 132072.28 (629.28 seconds)\n", + "Cbc0010I After 43500 nodes, 17139 on tree, 137528.52 best solution, best possible 132072.28 (630.43 seconds)\n", + "Cbc0010I After 43600 nodes, 17188 on tree, 137528.52 best solution, best possible 132072.28 (631.56 seconds)\n", + "Cbc0010I After 43700 nodes, 17237 on tree, 137528.52 best solution, best possible 132072.28 (632.76 seconds)\n", + "Cbc0010I After 43800 nodes, 17283 on tree, 137528.52 best solution, best possible 132072.28 (633.79 seconds)\n", + "Cbc0010I After 43900 nodes, 17333 on tree, 137528.52 best solution, best possible 132072.28 (635.07 seconds)\n", + "Cbc0010I After 44000 nodes, 17381 on tree, 137528.52 best solution, best possible 132072.28 (637.28 seconds)\n", + "Cbc0010I After 44100 nodes, 17389 on tree, 137528.52 best solution, best possible 132072.28 (638.09 seconds)\n", + "Cbc0010I After 44200 nodes, 17382 on tree, 137528.52 best solution, best possible 132072.28 (638.55 seconds)\n", + "Cbc0010I After 44300 nodes, 17395 on tree, 137528.52 best solution, best possible 132072.28 (638.97 seconds)\n", + "Cbc0010I After 44400 nodes, 17398 on tree, 137528.52 best solution, best possible 132072.28 (639.37 seconds)\n", + "Cbc0012I Integer solution of 136602.02 found by heuristic after 2419418 iterations and 44414 nodes (639.44 seconds)\n", + "Cbc0010I After 44500 nodes, 16539 on tree, 136602.02 best solution, best possible 132072.28 (640.65 seconds)\n", + "Cbc0010I After 44600 nodes, 16586 on tree, 136602.02 best solution, best possible 132072.28 (641.83 seconds)\n", + "Cbc0010I After 44700 nodes, 16632 on tree, 136602.02 best solution, best possible 132072.28 (643.15 seconds)\n", + "Cbc0010I After 44800 nodes, 16678 on tree, 136602.02 best solution, best possible 132072.28 (644.52 seconds)\n", + "Cbc0010I After 44900 nodes, 16722 on tree, 136602.02 best solution, best possible 132072.28 (645.55 seconds)\n", + "Cbc0010I After 45000 nodes, 16769 on tree, 136602.02 best solution, best possible 132072.28 (646.81 seconds)\n", + "Cbc0010I After 45100 nodes, 16819 on tree, 136602.02 best solution, best possible 132076.03 (648.14 seconds)\n", + "Cbc0010I After 45200 nodes, 16868 on tree, 136602.02 best solution, best possible 132079.78 (650.81 seconds)\n", + "Cbc0010I After 45300 nodes, 16915 on tree, 136602.02 best solution, best possible 132083.15 (652.35 seconds)\n", + "Cbc0010I After 45400 nodes, 16958 on tree, 136602.02 best solution, best possible 132086.78 (653.64 seconds)\n", + "Cbc0010I After 45500 nodes, 17005 on tree, 136602.02 best solution, best possible 132089.91 (654.91 seconds)\n", + "Cbc0010I After 45600 nodes, 17052 on tree, 136602.02 best solution, best possible 132093.46 (656.30 seconds)\n", + "Cbc0010I After 45700 nodes, 17100 on tree, 136602.02 best solution, best possible 132096.69 (657.81 seconds)\n", + "Cbc0010I After 45800 nodes, 17149 on tree, 136602.02 best solution, best possible 132099.93 (659.28 seconds)\n", + "Cbc0010I After 45900 nodes, 17196 on tree, 136602.02 best solution, best possible 132103.35 (660.81 seconds)\n", + "Cbc0010I After 46000 nodes, 17244 on tree, 136602.02 best solution, best possible 132106.8 (663.11 seconds)\n", + "Cbc0010I After 46100 nodes, 17289 on tree, 136602.02 best solution, best possible 132106.94 (664.91 seconds)\n", + "Cbc0010I After 46200 nodes, 17335 on tree, 136602.02 best solution, best possible 132106.94 (666.14 seconds)\n", + "Cbc0010I After 46300 nodes, 17381 on tree, 136602.02 best solution, best possible 132106.94 (667.40 seconds)\n", + "Cbc0010I After 46400 nodes, 17425 on tree, 136602.02 best solution, best possible 132106.94 (668.76 seconds)\n", + "Cbc0010I After 46500 nodes, 17467 on tree, 136602.02 best solution, best possible 132106.94 (669.91 seconds)\n", + "Cbc0010I After 46600 nodes, 17516 on tree, 136602.02 best solution, best possible 132106.94 (671.30 seconds)\n", + "Cbc0010I After 46700 nodes, 17562 on tree, 136602.02 best solution, best possible 132106.94 (672.62 seconds)\n", + "Cbc0010I After 46800 nodes, 17609 on tree, 136602.02 best solution, best possible 132106.94 (673.69 seconds)\n", + "Cbc0010I After 46900 nodes, 17653 on tree, 136602.02 best solution, best possible 132106.94 (675.43 seconds)\n", + "Cbc0010I After 47000 nodes, 17695 on tree, 136602.02 best solution, best possible 132106.94 (677.28 seconds)\n", + "Cbc0010I After 47100 nodes, 17743 on tree, 136602.02 best solution, best possible 132106.94 (678.57 seconds)\n", + "Cbc0010I After 47200 nodes, 17787 on tree, 136602.02 best solution, best possible 132106.95 (679.77 seconds)\n", + "Cbc0010I After 47300 nodes, 17835 on tree, 136602.02 best solution, best possible 132106.95 (681.00 seconds)\n", + "Cbc0010I After 47400 nodes, 17882 on tree, 136602.02 best solution, best possible 132106.95 (682.19 seconds)\n", + "Cbc0010I After 47500 nodes, 17929 on tree, 136602.02 best solution, best possible 132106.95 (683.40 seconds)\n", + "Cbc0010I After 47600 nodes, 17977 on tree, 136602.02 best solution, best possible 132106.95 (684.95 seconds)\n", + "Cbc0010I After 47700 nodes, 18021 on tree, 136602.02 best solution, best possible 132106.95 (685.98 seconds)\n", + "Cbc0010I After 47800 nodes, 18064 on tree, 136602.02 best solution, best possible 132106.95 (687.42 seconds)\n", + "Cbc0010I After 47900 nodes, 18109 on tree, 136602.02 best solution, best possible 132106.95 (689.74 seconds)\n", + "Cbc0010I After 48000 nodes, 18156 on tree, 136602.02 best solution, best possible 132106.95 (691.17 seconds)\n", + "Cbc0010I After 48100 nodes, 18154 on tree, 136602.02 best solution, best possible 132106.95 (691.68 seconds)\n", + "Cbc0010I After 48200 nodes, 18153 on tree, 136602.02 best solution, best possible 132106.95 (692.20 seconds)\n", + "Cbc0012I Integer solution of 136554.19 found by heuristic after 2648546 iterations and 48273 nodes (692.54 seconds)\n", + "Cbc0010I After 48300 nodes, 18098 on tree, 136554.19 best solution, best possible 132106.95 (692.81 seconds)\n", + "Cbc0010I After 48400 nodes, 18144 on tree, 136554.19 best solution, best possible 132106.95 (693.97 seconds)\n", + "Cbc0010I After 48500 nodes, 18190 on tree, 136554.19 best solution, best possible 132106.95 (695.30 seconds)\n", + "Cbc0010I After 48600 nodes, 18235 on tree, 136554.19 best solution, best possible 132106.95 (696.57 seconds)\n", + "Cbc0010I After 48700 nodes, 18281 on tree, 136554.19 best solution, best possible 132106.95 (697.78 seconds)\n", + "Cbc0010I After 48800 nodes, 18327 on tree, 136554.19 best solution, best possible 132106.95 (699.04 seconds)\n", + "Cbc0010I After 48900 nodes, 18371 on tree, 136554.19 best solution, best possible 132106.95 (700.38 seconds)\n", + "Cbc0010I After 49000 nodes, 18418 on tree, 136554.19 best solution, best possible 132106.95 (702.62 seconds)\n", + "Cbc0010I After 49100 nodes, 18416 on tree, 136554.19 best solution, best possible 132106.95 (703.31 seconds)\n", + "Cbc0010I After 49200 nodes, 18404 on tree, 136554.19 best solution, best possible 132106.95 (703.92 seconds)\n", + "Cbc0010I After 49300 nodes, 18392 on tree, 136554.19 best solution, best possible 132106.95 (704.48 seconds)\n", + "Cbc0010I After 49400 nodes, 18380 on tree, 136554.19 best solution, best possible 132106.95 (705.01 seconds)\n", + "Cbc0010I After 49500 nodes, 18367 on tree, 136554.19 best solution, best possible 132106.95 (705.56 seconds)\n", + "Cbc0010I After 49600 nodes, 18375 on tree, 136554.19 best solution, best possible 132106.95 (706.26 seconds)\n", + "Cbc0010I After 49700 nodes, 18370 on tree, 136554.19 best solution, best possible 132106.95 (706.78 seconds)\n", + "Cbc0010I After 49800 nodes, 18348 on tree, 136554.19 best solution, best possible 132106.95 (707.24 seconds)\n", + "Cbc0010I After 49900 nodes, 18328 on tree, 136554.19 best solution, best possible 132106.95 (707.70 seconds)\n", + "Cbc0010I After 50000 nodes, 18333 on tree, 136554.19 best solution, best possible 132106.95 (708.21 seconds)\n", + "Cbc0010I After 50100 nodes, 18331 on tree, 136554.19 best solution, best possible 132106.95 (708.71 seconds)\n", + "Cbc0010I After 50200 nodes, 18302 on tree, 136554.19 best solution, best possible 132106.95 (709.22 seconds)\n", + "Cbc0010I After 50300 nodes, 18295 on tree, 136554.19 best solution, best possible 132106.95 (709.80 seconds)\n", + "Cbc0010I After 50400 nodes, 18293 on tree, 136554.19 best solution, best possible 132106.95 (710.37 seconds)\n", + "Cbc0010I After 50500 nodes, 18291 on tree, 136554.19 best solution, best possible 132106.95 (710.90 seconds)\n", + "Cbc0010I After 50600 nodes, 18269 on tree, 136554.19 best solution, best possible 132106.95 (711.49 seconds)\n", + "Cbc0010I After 50700 nodes, 18255 on tree, 136554.19 best solution, best possible 132106.95 (712.03 seconds)\n", + "Cbc0010I After 50800 nodes, 18255 on tree, 136554.19 best solution, best possible 132106.95 (712.60 seconds)\n", + "Cbc0010I After 50900 nodes, 18255 on tree, 136554.19 best solution, best possible 132106.95 (713.42 seconds)\n", + "Cbc0010I After 51000 nodes, 18255 on tree, 136554.19 best solution, best possible 132106.95 (714.49 seconds)\n", + "Cbc0010I After 51100 nodes, 18254 on tree, 136554.19 best solution, best possible 132106.95 (715.53 seconds)\n", + "Cbc0010I After 51200 nodes, 18238 on tree, 136554.19 best solution, best possible 132106.95 (716.09 seconds)\n", + "Cbc0010I After 51300 nodes, 18218 on tree, 136554.19 best solution, best possible 132106.95 (716.64 seconds)\n", + "Cbc0010I After 51400 nodes, 18210 on tree, 136554.19 best solution, best possible 132106.95 (717.16 seconds)\n", + "Cbc0010I After 51500 nodes, 18210 on tree, 136554.19 best solution, best possible 132106.95 (717.70 seconds)\n", + "Cbc0010I After 51600 nodes, 18211 on tree, 136554.19 best solution, best possible 132106.95 (718.30 seconds)\n", + "Cbc0010I After 51700 nodes, 18209 on tree, 136554.19 best solution, best possible 132106.95 (718.93 seconds)\n", + "Cbc0010I After 51800 nodes, 18187 on tree, 136554.19 best solution, best possible 132106.95 (719.52 seconds)\n", + "Cbc0010I After 51900 nodes, 18161 on tree, 136554.19 best solution, best possible 132106.95 (720.16 seconds)\n", + "Cbc0010I After 52000 nodes, 18168 on tree, 136554.19 best solution, best possible 132106.95 (720.63 seconds)\n", + "Cbc0010I After 52100 nodes, 18169 on tree, 136554.19 best solution, best possible 132106.95 (721.07 seconds)\n", + "Cbc0012I Integer solution of 136107.91 found by heuristic after 2808104 iterations and 52135 nodes (721.22 seconds)\n", + "Cbc0010I After 52200 nodes, 17604 on tree, 136107.91 best solution, best possible 132106.95 (722.41 seconds)\n", + "Cbc0010I After 52300 nodes, 17652 on tree, 136107.91 best solution, best possible 132106.95 (723.95 seconds)\n", + "Cbc0010I After 52400 nodes, 17698 on tree, 136107.91 best solution, best possible 132106.95 (725.30 seconds)\n", + "Cbc0010I After 52500 nodes, 17744 on tree, 136107.91 best solution, best possible 132106.95 (727.43 seconds)\n", + "Cbc0010I After 52600 nodes, 17792 on tree, 136107.91 best solution, best possible 132106.95 (729.47 seconds)\n", + "Cbc0010I After 52700 nodes, 17841 on tree, 136107.91 best solution, best possible 132106.95 (731.22 seconds)\n", + "Cbc0010I After 52800 nodes, 17886 on tree, 136107.91 best solution, best possible 132106.95 (732.78 seconds)\n", + "Cbc0010I After 52900 nodes, 17933 on tree, 136107.91 best solution, best possible 132106.95 (734.06 seconds)\n", + "Cbc0010I After 53000 nodes, 17976 on tree, 136107.91 best solution, best possible 132106.95 (735.56 seconds)\n", + "Cbc0010I After 53100 nodes, 18024 on tree, 136107.91 best solution, best possible 132111.66 (737.08 seconds)\n", + "Cbc0010I After 53200 nodes, 18070 on tree, 136107.91 best solution, best possible 132114.99 (739.17 seconds)\n", + "Cbc0010I After 53300 nodes, 18116 on tree, 136107.91 best solution, best possible 132119.21 (741.69 seconds)\n", + "Cbc0010I After 53400 nodes, 18164 on tree, 136107.91 best solution, best possible 132123.16 (743.17 seconds)\n", + "Cbc0010I After 53500 nodes, 18211 on tree, 136107.91 best solution, best possible 132125.94 (744.74 seconds)\n", + "Cbc0010I After 53600 nodes, 18256 on tree, 136107.91 best solution, best possible 132128.91 (746.44 seconds)\n", + "Cbc0010I After 53700 nodes, 18306 on tree, 136107.91 best solution, best possible 132131.97 (747.78 seconds)\n", + "Cbc0010I After 53800 nodes, 18353 on tree, 136107.91 best solution, best possible 132135.74 (749.43 seconds)\n", + "Cbc0010I After 53900 nodes, 18401 on tree, 136107.91 best solution, best possible 132139.34 (751.00 seconds)\n", + "Cbc0010I After 54000 nodes, 18449 on tree, 136107.91 best solution, best possible 132141.81 (753.35 seconds)\n", + "Cbc0010I After 54100 nodes, 18439 on tree, 136107.91 best solution, best possible 132141.81 (754.08 seconds)\n", + "Cbc0010I After 54200 nodes, 18439 on tree, 136107.91 best solution, best possible 132141.81 (754.53 seconds)\n", + "Cbc0010I After 54300 nodes, 18444 on tree, 136107.91 best solution, best possible 132141.81 (755.10 seconds)\n", + "Cbc0010I After 54400 nodes, 18410 on tree, 136107.91 best solution, best possible 132141.81 (755.57 seconds)\n", + "Cbc0010I After 54500 nodes, 18408 on tree, 136107.91 best solution, best possible 132141.81 (756.04 seconds)\n", + "Cbc0010I After 54600 nodes, 18411 on tree, 136107.91 best solution, best possible 132141.81 (756.46 seconds)\n", + "Cbc0010I After 54700 nodes, 18407 on tree, 136107.91 best solution, best possible 132141.81 (756.96 seconds)\n", + "Cbc0010I After 54800 nodes, 18403 on tree, 136107.91 best solution, best possible 132141.81 (757.43 seconds)\n", + "Cbc0010I After 54900 nodes, 18405 on tree, 136107.91 best solution, best possible 132141.81 (757.94 seconds)\n", + "Cbc0010I After 55000 nodes, 18399 on tree, 136107.91 best solution, best possible 132141.81 (758.50 seconds)\n", + "Cbc0010I After 55100 nodes, 18363 on tree, 136107.91 best solution, best possible 132141.81 (759.09 seconds)\n", + "Cbc0010I After 55200 nodes, 18342 on tree, 136107.91 best solution, best possible 132141.81 (759.69 seconds)\n", + "Cbc0010I After 55300 nodes, 18339 on tree, 136107.91 best solution, best possible 132141.81 (760.16 seconds)\n", + "Cbc0010I After 55400 nodes, 18338 on tree, 136107.91 best solution, best possible 132141.81 (760.74 seconds)\n", + "Cbc0010I After 55500 nodes, 18341 on tree, 136107.91 best solution, best possible 132141.81 (761.53 seconds)\n", + "Cbc0010I After 55600 nodes, 18315 on tree, 136107.91 best solution, best possible 132141.81 (762.13 seconds)\n", + "Cbc0010I After 55700 nodes, 18280 on tree, 136107.91 best solution, best possible 132141.81 (762.97 seconds)\n", + "Cbc0010I After 55800 nodes, 18282 on tree, 136107.91 best solution, best possible 132141.81 (763.57 seconds)\n", + "Cbc0010I After 55900 nodes, 18282 on tree, 136107.91 best solution, best possible 132141.81 (764.33 seconds)\n", + "Cbc0010I After 56000 nodes, 18281 on tree, 136107.91 best solution, best possible 132141.81 (765.52 seconds)\n", + "Cbc0010I After 56100 nodes, 18282 on tree, 136107.91 best solution, best possible 132141.81 (766.83 seconds)\n", + "Cbc0010I After 56200 nodes, 18295 on tree, 136107.91 best solution, best possible 132141.81 (767.51 seconds)\n", + "Cbc0010I After 56300 nodes, 18286 on tree, 136107.91 best solution, best possible 132141.81 (767.95 seconds)\n", + "Cbc0010I After 56400 nodes, 18283 on tree, 136107.91 best solution, best possible 132141.81 (768.51 seconds)\n", + "Cbc0010I After 56500 nodes, 18270 on tree, 136107.91 best solution, best possible 132141.81 (769.17 seconds)\n", + "Cbc0010I After 56600 nodes, 18250 on tree, 136107.91 best solution, best possible 132141.81 (769.72 seconds)\n", + "Cbc0010I After 56700 nodes, 18228 on tree, 136107.91 best solution, best possible 132141.81 (770.35 seconds)\n", + "Cbc0010I After 56800 nodes, 18220 on tree, 136107.91 best solution, best possible 132141.81 (770.96 seconds)\n", + "Cbc0010I After 56900 nodes, 18222 on tree, 136107.91 best solution, best possible 132141.81 (771.42 seconds)\n", + "Cbc0010I After 57000 nodes, 18222 on tree, 136107.91 best solution, best possible 132141.81 (771.96 seconds)\n", + "Cbc0010I After 57100 nodes, 18270 on tree, 136107.91 best solution, best possible 132144.99 (773.48 seconds)\n", + "Cbc0010I After 57200 nodes, 18316 on tree, 136107.91 best solution, best possible 132148.49 (775.34 seconds)\n", + "Cbc0010I After 57300 nodes, 18361 on tree, 136107.91 best solution, best possible 132152.59 (777.00 seconds)\n", + "Cbc0010I After 57400 nodes, 18408 on tree, 136107.91 best solution, best possible 132155.78 (779.74 seconds)\n", + "Cbc0010I After 57500 nodes, 18453 on tree, 136107.91 best solution, best possible 132159.2 (781.13 seconds)\n", + "Cbc0010I After 57600 nodes, 18500 on tree, 136107.91 best solution, best possible 132161.84 (782.83 seconds)\n", + "Cbc0010I After 57700 nodes, 18543 on tree, 136107.91 best solution, best possible 132164.18 (784.47 seconds)\n", + "Cbc0010I After 57800 nodes, 18591 on tree, 136107.91 best solution, best possible 132166.64 (786.37 seconds)\n", + "Cbc0010I After 57900 nodes, 18637 on tree, 136107.91 best solution, best possible 132169.84 (787.84 seconds)\n", + "Cbc0010I After 58000 nodes, 18683 on tree, 136107.91 best solution, best possible 132172.53 (789.47 seconds)\n", + "Cbc0010I After 58100 nodes, 18682 on tree, 136107.91 best solution, best possible 132172.6 (790.37 seconds)\n", + "Cbc0010I After 58200 nodes, 18685 on tree, 136107.91 best solution, best possible 132172.6 (791.37 seconds)\n", + "Cbc0010I After 58300 nodes, 18676 on tree, 136107.91 best solution, best possible 132172.6 (792.34 seconds)\n", + "Cbc0010I After 58400 nodes, 18632 on tree, 136107.91 best solution, best possible 132172.6 (792.88 seconds)\n", + "Cbc0010I After 58500 nodes, 18616 on tree, 136107.91 best solution, best possible 132172.6 (793.40 seconds)\n", + "Cbc0010I After 58600 nodes, 18617 on tree, 136107.91 best solution, best possible 132172.6 (793.84 seconds)\n", + "Cbc0010I After 58700 nodes, 18615 on tree, 136107.91 best solution, best possible 132172.6 (794.32 seconds)\n", + "Cbc0010I After 58800 nodes, 18616 on tree, 136107.91 best solution, best possible 132172.6 (794.79 seconds)\n", + "Cbc0010I After 58900 nodes, 18618 on tree, 136107.91 best solution, best possible 132172.6 (795.47 seconds)\n", + "Cbc0010I After 59000 nodes, 18620 on tree, 136107.91 best solution, best possible 132172.6 (796.02 seconds)\n", + "Cbc0010I After 59100 nodes, 18625 on tree, 136107.91 best solution, best possible 132172.6 (796.54 seconds)\n", + "Cbc0010I After 59200 nodes, 18621 on tree, 136107.91 best solution, best possible 132172.6 (796.99 seconds)\n", + "Cbc0010I After 59300 nodes, 18616 on tree, 136107.91 best solution, best possible 132172.6 (797.61 seconds)\n", + "Cbc0010I After 59400 nodes, 18616 on tree, 136107.91 best solution, best possible 132172.6 (798.24 seconds)\n", + "Cbc0010I After 59500 nodes, 18611 on tree, 136107.91 best solution, best possible 132172.6 (799.03 seconds)\n", + "Cbc0010I After 59600 nodes, 18582 on tree, 136107.91 best solution, best possible 132172.6 (799.52 seconds)\n", + "Cbc0010I After 59700 nodes, 18564 on tree, 136107.91 best solution, best possible 132172.6 (800.09 seconds)\n", + "Cbc0010I After 59800 nodes, 18538 on tree, 136107.91 best solution, best possible 132172.6 (800.63 seconds)\n", + "Cbc0010I After 59900 nodes, 18541 on tree, 136107.91 best solution, best possible 132172.6 (801.05 seconds)\n", + "Cbc0010I After 60000 nodes, 18540 on tree, 136107.91 best solution, best possible 132172.6 (801.48 seconds)\n", + "Cbc0010I After 60100 nodes, 18538 on tree, 136107.91 best solution, best possible 132172.6 (801.95 seconds)\n", + "Cbc0010I After 60200 nodes, 18542 on tree, 136107.91 best solution, best possible 132172.6 (802.44 seconds)\n", + "Cbc0010I After 60300 nodes, 18538 on tree, 136107.91 best solution, best possible 132172.6 (803.40 seconds)\n", + "Cbc0010I After 60400 nodes, 18539 on tree, 136107.91 best solution, best possible 132172.6 (804.21 seconds)\n", + "Cbc0010I After 60500 nodes, 18538 on tree, 136107.91 best solution, best possible 132172.6 (805.13 seconds)\n", + "Cbc0010I After 60600 nodes, 18538 on tree, 136107.91 best solution, best possible 132172.6 (805.58 seconds)\n", + "Cbc0010I After 60700 nodes, 18542 on tree, 136107.91 best solution, best possible 132172.6 (806.25 seconds)\n", + "Cbc0010I After 60800 nodes, 18541 on tree, 136107.91 best solution, best possible 132172.6 (806.75 seconds)\n", + "Cbc0010I After 60900 nodes, 18542 on tree, 136107.91 best solution, best possible 132172.6 (807.24 seconds)\n", + "Cbc0010I After 61000 nodes, 18538 on tree, 136107.91 best solution, best possible 132172.6 (807.83 seconds)\n", + "Cbc0010I After 61100 nodes, 18539 on tree, 136107.91 best solution, best possible 132172.6 (808.31 seconds)\n", + "Cbc0010I After 61200 nodes, 18545 on tree, 136107.91 best solution, best possible 132172.6 (808.94 seconds)\n", + "Cbc0010I After 61300 nodes, 18538 on tree, 136107.91 best solution, best possible 132172.6 (809.52 seconds)\n", + "Cbc0010I After 61400 nodes, 18537 on tree, 136107.91 best solution, best possible 132172.6 (810.10 seconds)\n", + "Cbc0010I After 61500 nodes, 18538 on tree, 136107.91 best solution, best possible 132172.6 (810.70 seconds)\n", + "Cbc0010I After 61600 nodes, 18539 on tree, 136107.91 best solution, best possible 132172.6 (811.59 seconds)\n", + "Cbc0010I After 61700 nodes, 18537 on tree, 136107.91 best solution, best possible 132172.6 (812.41 seconds)\n", + "Cbc0010I After 61800 nodes, 18492 on tree, 136107.91 best solution, best possible 132172.6 (812.95 seconds)\n", + "Cbc0010I After 61900 nodes, 18472 on tree, 136107.91 best solution, best possible 132172.6 (813.45 seconds)\n", + "Cbc0010I After 62000 nodes, 18454 on tree, 136107.91 best solution, best possible 132172.6 (814.00 seconds)\n", + "Cbc0010I After 62100 nodes, 18446 on tree, 136107.91 best solution, best possible 132172.6 (814.62 seconds)\n", + "Cbc0010I After 62200 nodes, 18423 on tree, 136107.91 best solution, best possible 132172.6 (815.38 seconds)\n", + "Cbc0010I After 62300 nodes, 18400 on tree, 136107.91 best solution, best possible 132172.6 (816.63 seconds)\n", + "Cbc0010I After 62400 nodes, 18399 on tree, 136107.91 best solution, best possible 132172.6 (817.77 seconds)\n", + "Cbc0010I After 62500 nodes, 18393 on tree, 136107.91 best solution, best possible 132172.6 (818.41 seconds)\n", + "Cbc0010I After 62600 nodes, 18385 on tree, 136107.91 best solution, best possible 132172.6 (819.04 seconds)\n", + "Cbc0010I After 62700 nodes, 18387 on tree, 136107.91 best solution, best possible 132172.6 (819.74 seconds)\n", + "Cbc0010I After 62800 nodes, 18386 on tree, 136107.91 best solution, best possible 132172.6 (820.34 seconds)\n", + "Cbc0010I After 62900 nodes, 18388 on tree, 136107.91 best solution, best possible 132172.6 (821.01 seconds)\n", + "Cbc0010I After 63000 nodes, 18396 on tree, 136107.91 best solution, best possible 132172.6 (821.57 seconds)\n", + "Cbc0010I After 63100 nodes, 18387 on tree, 136107.91 best solution, best possible 132172.6 (822.20 seconds)\n", + "Cbc0010I After 63200 nodes, 18391 on tree, 136107.91 best solution, best possible 132172.6 (822.91 seconds)\n", + "Cbc0010I After 63300 nodes, 18389 on tree, 136107.91 best solution, best possible 132172.6 (823.44 seconds)\n", + "Cbc0010I After 63400 nodes, 18392 on tree, 136107.91 best solution, best possible 132172.6 (824.09 seconds)\n", + "Cbc0010I After 63500 nodes, 18387 on tree, 136107.91 best solution, best possible 132172.6 (824.82 seconds)\n", + "Cbc0010I After 63600 nodes, 18391 on tree, 136107.91 best solution, best possible 132172.6 (825.50 seconds)\n", + "Cbc0010I After 63700 nodes, 18390 on tree, 136107.91 best solution, best possible 132172.6 (826.16 seconds)\n", + "Cbc0010I After 63800 nodes, 18393 on tree, 136107.91 best solution, best possible 132172.6 (826.94 seconds)\n", + "Cbc0010I After 63900 nodes, 18389 on tree, 136107.91 best solution, best possible 132172.6 (827.65 seconds)\n", + "Cbc0010I After 64000 nodes, 18385 on tree, 136107.91 best solution, best possible 132172.6 (828.59 seconds)\n", + "Cbc0010I After 64100 nodes, 18388 on tree, 136107.91 best solution, best possible 132172.6 (829.88 seconds)\n", + "Cbc0010I After 64200 nodes, 18392 on tree, 136107.91 best solution, best possible 132172.6 (830.96 seconds)\n", + "Cbc0010I After 64300 nodes, 18386 on tree, 136107.91 best solution, best possible 132172.6 (831.88 seconds)\n", + "Cbc0010I After 64400 nodes, 18391 on tree, 136107.91 best solution, best possible 132172.6 (832.71 seconds)\n", + "Cbc0010I After 64500 nodes, 18351 on tree, 136107.91 best solution, best possible 132172.6 (833.34 seconds)\n", + "Cbc0010I After 64600 nodes, 18323 on tree, 136107.91 best solution, best possible 132172.6 (833.95 seconds)\n", + "Cbc0010I After 64700 nodes, 18307 on tree, 136107.91 best solution, best possible 132172.6 (834.61 seconds)\n", + "Cbc0010I After 64800 nodes, 18308 on tree, 136107.91 best solution, best possible 132172.6 (835.18 seconds)\n", + "Cbc0010I After 64900 nodes, 18288 on tree, 136107.91 best solution, best possible 132172.6 (835.73 seconds)\n", + "Cbc0010I After 65000 nodes, 18271 on tree, 136107.91 best solution, best possible 132172.6 (836.66 seconds)\n", + "Cbc0010I After 65100 nodes, 18271 on tree, 136107.91 best solution, best possible 132172.6 (837.24 seconds)\n", + "Cbc0010I After 65200 nodes, 18271 on tree, 136107.91 best solution, best possible 132172.6 (838.14 seconds)\n", + "Cbc0010I After 65300 nodes, 18263 on tree, 136107.91 best solution, best possible 132172.6 (838.86 seconds)\n", + "Cbc0010I After 65400 nodes, 18259 on tree, 136107.91 best solution, best possible 132172.6 (839.50 seconds)\n", + "Cbc0010I After 65500 nodes, 18258 on tree, 136107.91 best solution, best possible 132172.6 (840.04 seconds)\n", + "Cbc0010I After 65600 nodes, 18256 on tree, 136107.91 best solution, best possible 132172.6 (840.73 seconds)\n", + "Cbc0010I After 65700 nodes, 18263 on tree, 136107.91 best solution, best possible 132172.6 (841.81 seconds)\n", + "Cbc0010I After 65800 nodes, 18264 on tree, 136107.91 best solution, best possible 132172.6 (843.08 seconds)\n", + "Cbc0010I After 65900 nodes, 18264 on tree, 136107.91 best solution, best possible 132172.6 (843.83 seconds)\n", + "Cbc0010I After 66000 nodes, 18260 on tree, 136107.91 best solution, best possible 132172.6 (844.28 seconds)\n", + "Cbc0010I After 66100 nodes, 18260 on tree, 136107.91 best solution, best possible 132172.6 (844.86 seconds)\n", + "Cbc0010I After 66200 nodes, 18267 on tree, 136107.91 best solution, best possible 132172.6 (845.49 seconds)\n", + "Cbc0010I After 66300 nodes, 18263 on tree, 136107.91 best solution, best possible 132172.6 (846.05 seconds)\n", + "Cbc0010I After 66400 nodes, 18265 on tree, 136107.91 best solution, best possible 132172.6 (846.73 seconds)\n", + "Cbc0010I After 66500 nodes, 18269 on tree, 136107.91 best solution, best possible 132172.6 (847.32 seconds)\n", + "Cbc0010I After 66600 nodes, 18267 on tree, 136107.91 best solution, best possible 132172.6 (847.96 seconds)\n", + "Cbc0010I After 66700 nodes, 18262 on tree, 136107.91 best solution, best possible 132172.6 (848.61 seconds)\n", + "Cbc0010I After 66800 nodes, 18268 on tree, 136107.91 best solution, best possible 132172.6 (849.27 seconds)\n", + "Cbc0010I After 66900 nodes, 18264 on tree, 136107.91 best solution, best possible 132172.6 (849.91 seconds)\n", + "Cbc0010I After 67000 nodes, 18261 on tree, 136107.91 best solution, best possible 132172.6 (850.59 seconds)\n", + "Cbc0010I After 67100 nodes, 18264 on tree, 136107.91 best solution, best possible 132172.6 (851.19 seconds)\n", + "Cbc0010I After 67200 nodes, 18258 on tree, 136107.91 best solution, best possible 132172.6 (851.81 seconds)\n", + "Cbc0010I After 67300 nodes, 18265 on tree, 136107.91 best solution, best possible 132172.6 (852.52 seconds)\n", + "Cbc0010I After 67400 nodes, 18261 on tree, 136107.91 best solution, best possible 132172.6 (853.10 seconds)\n", + "Cbc0010I After 67500 nodes, 18260 on tree, 136107.91 best solution, best possible 132172.6 (853.84 seconds)\n", + "Cbc0010I After 67600 nodes, 18265 on tree, 136107.91 best solution, best possible 132172.6 (855.09 seconds)\n", + "Cbc0010I After 67700 nodes, 18263 on tree, 136107.91 best solution, best possible 132172.6 (856.42 seconds)\n", + "Cbc0010I After 67800 nodes, 18259 on tree, 136107.91 best solution, best possible 132172.6 (857.15 seconds)\n", + "Cbc0010I After 67900 nodes, 18259 on tree, 136107.91 best solution, best possible 132172.6 (857.87 seconds)\n", + "Cbc0010I After 68000 nodes, 18262 on tree, 136107.91 best solution, best possible 132172.6 (858.59 seconds)\n", + "Cbc0010I After 68100 nodes, 18260 on tree, 136107.91 best solution, best possible 132172.6 (859.40 seconds)\n", + "Cbc0010I After 68200 nodes, 18273 on tree, 136107.91 best solution, best possible 132172.6 (860.43 seconds)\n", + "Cbc0010I After 68300 nodes, 18262 on tree, 136107.91 best solution, best possible 132172.6 (860.96 seconds)\n", + "Cbc0010I After 68400 nodes, 18267 on tree, 136107.91 best solution, best possible 132172.6 (861.65 seconds)\n", + "Cbc0010I After 68500 nodes, 18264 on tree, 136107.91 best solution, best possible 132172.6 (862.22 seconds)\n", + "Cbc0010I After 68600 nodes, 18260 on tree, 136107.91 best solution, best possible 132172.6 (862.74 seconds)\n", + "Cbc0010I After 68700 nodes, 18243 on tree, 136107.91 best solution, best possible 132172.6 (863.48 seconds)\n", + "Cbc0010I After 68800 nodes, 18226 on tree, 136107.91 best solution, best possible 132172.6 (864.11 seconds)\n", + "Cbc0010I After 68900 nodes, 18199 on tree, 136107.91 best solution, best possible 132172.6 (864.78 seconds)\n", + "Cbc0010I After 69000 nodes, 18193 on tree, 136107.91 best solution, best possible 132172.6 (865.29 seconds)\n", + "Cbc0010I After 69100 nodes, 18238 on tree, 136107.91 best solution, best possible 132175.17 (867.95 seconds)\n", + "Cbc0010I After 69200 nodes, 18283 on tree, 136107.91 best solution, best possible 132178.16 (870.43 seconds)\n", + "Cbc0010I After 69300 nodes, 18329 on tree, 136107.91 best solution, best possible 132181.27 (872.53 seconds)\n", + "Cbc0010I After 69400 nodes, 18373 on tree, 136107.91 best solution, best possible 132183.89 (874.36 seconds)\n", + "Cbc0010I After 69500 nodes, 18417 on tree, 136107.91 best solution, best possible 132186.9 (875.96 seconds)\n", + "Cbc0010I After 69600 nodes, 18462 on tree, 136107.91 best solution, best possible 132190.86 (877.47 seconds)\n", + "Cbc0010I After 69700 nodes, 18507 on tree, 136107.91 best solution, best possible 132193.69 (879.22 seconds)\n", + "Cbc0010I After 69800 nodes, 18553 on tree, 136107.91 best solution, best possible 132196.76 (882.03 seconds)\n", + "Cbc0010I After 69900 nodes, 18595 on tree, 136107.91 best solution, best possible 132199.79 (883.92 seconds)\n", + "Cbc0010I After 70000 nodes, 18640 on tree, 136107.91 best solution, best possible 132202.53 (885.66 seconds)\n", + "Cbc0010I After 70100 nodes, 18620 on tree, 136107.91 best solution, best possible 132202.59 (886.33 seconds)\n", + "Cbc0010I After 70200 nodes, 18617 on tree, 136107.91 best solution, best possible 132202.59 (886.77 seconds)\n", + "Cbc0010I After 70300 nodes, 18596 on tree, 136107.91 best solution, best possible 132202.59 (887.35 seconds)\n", + "Cbc0010I After 70400 nodes, 18597 on tree, 136107.91 best solution, best possible 132202.59 (888.09 seconds)\n", + "Cbc0010I After 70500 nodes, 18595 on tree, 136107.91 best solution, best possible 132202.59 (888.68 seconds)\n", + "Cbc0010I After 70600 nodes, 18586 on tree, 136107.91 best solution, best possible 132202.59 (889.30 seconds)\n", + "Cbc0010I After 70700 nodes, 18571 on tree, 136107.91 best solution, best possible 132202.59 (890.19 seconds)\n", + "Cbc0010I After 70800 nodes, 18575 on tree, 136107.91 best solution, best possible 132202.59 (891.08 seconds)\n", + "Cbc0010I After 70900 nodes, 18572 on tree, 136107.91 best solution, best possible 132202.59 (891.66 seconds)\n", + "Cbc0010I After 71000 nodes, 18568 on tree, 136107.91 best solution, best possible 132202.59 (892.73 seconds)\n", + "Cbc0010I After 71100 nodes, 18566 on tree, 136107.91 best solution, best possible 132202.59 (893.66 seconds)\n", + "Cbc0010I After 71200 nodes, 18567 on tree, 136107.91 best solution, best possible 132202.59 (894.71 seconds)\n", + "Cbc0010I After 71300 nodes, 18572 on tree, 136107.91 best solution, best possible 132202.59 (895.23 seconds)\n", + "Cbc0010I After 71400 nodes, 18567 on tree, 136107.91 best solution, best possible 132202.59 (895.79 seconds)\n", + "Cbc0010I After 71500 nodes, 18566 on tree, 136107.91 best solution, best possible 132202.59 (896.42 seconds)\n", + "Cbc0010I After 71600 nodes, 18572 on tree, 136107.91 best solution, best possible 132202.59 (897.06 seconds)\n", + "Cbc0010I After 71700 nodes, 18566 on tree, 136107.91 best solution, best possible 132202.59 (897.67 seconds)\n", + "Cbc0010I After 71800 nodes, 18569 on tree, 136107.91 best solution, best possible 132202.59 (898.31 seconds)\n", + "Cbc0010I After 71900 nodes, 18571 on tree, 136107.91 best solution, best possible 132202.59 (898.96 seconds)\n", + "Cbc0010I After 72000 nodes, 18574 on tree, 136107.91 best solution, best possible 132202.59 (899.53 seconds)\n", + "Cbc0010I After 72100 nodes, 18572 on tree, 136107.91 best solution, best possible 132202.59 (900.21 seconds)\n", + "Cbc0010I After 72200 nodes, 18564 on tree, 136107.91 best solution, best possible 132202.59 (900.69 seconds)\n", + "Cbc0010I After 72300 nodes, 18570 on tree, 136107.91 best solution, best possible 132202.59 (901.35 seconds)\n", + "Cbc0010I After 72400 nodes, 18569 on tree, 136107.91 best solution, best possible 132202.59 (902.10 seconds)\n", + "Cbc0010I After 72500 nodes, 18571 on tree, 136107.91 best solution, best possible 132202.59 (902.75 seconds)\n", + "Cbc0010I After 72600 nodes, 18569 on tree, 136107.91 best solution, best possible 132202.59 (903.69 seconds)\n", + "Cbc0010I After 72700 nodes, 18565 on tree, 136107.91 best solution, best possible 132202.59 (904.62 seconds)\n", + "Cbc0010I After 72800 nodes, 18573 on tree, 136107.91 best solution, best possible 132202.59 (906.24 seconds)\n", + "Cbc0010I After 72900 nodes, 18573 on tree, 136107.91 best solution, best possible 132202.59 (907.29 seconds)\n", + "Cbc0010I After 73000 nodes, 18573 on tree, 136107.91 best solution, best possible 132202.59 (908.13 seconds)\n", + "Cbc0010I After 73100 nodes, 18569 on tree, 136107.91 best solution, best possible 132202.59 (908.80 seconds)\n", + "Cbc0010I After 73200 nodes, 18567 on tree, 136107.91 best solution, best possible 132202.59 (909.61 seconds)\n", + "Cbc0010I After 73300 nodes, 18571 on tree, 136107.91 best solution, best possible 132202.59 (910.43 seconds)\n", + "Cbc0010I After 73400 nodes, 18572 on tree, 136107.91 best solution, best possible 132202.59 (911.33 seconds)\n", + "Cbc0010I After 73500 nodes, 18571 on tree, 136107.91 best solution, best possible 132202.59 (911.95 seconds)\n", + "Cbc0010I After 73600 nodes, 18567 on tree, 136107.91 best solution, best possible 132202.59 (912.61 seconds)\n", + "Cbc0010I After 73700 nodes, 18568 on tree, 136107.91 best solution, best possible 132202.59 (913.22 seconds)\n", + "Cbc0010I After 73800 nodes, 18571 on tree, 136107.91 best solution, best possible 132202.59 (913.98 seconds)\n", + "Cbc0010I After 73900 nodes, 18566 on tree, 136107.91 best solution, best possible 132202.59 (914.85 seconds)\n", + "Cbc0010I After 74000 nodes, 18567 on tree, 136107.91 best solution, best possible 132202.59 (915.39 seconds)\n", + "Cbc0010I After 74100 nodes, 18574 on tree, 136107.91 best solution, best possible 132202.59 (916.54 seconds)\n", + "Cbc0010I After 74200 nodes, 18570 on tree, 136107.91 best solution, best possible 132202.59 (917.57 seconds)\n", + "Cbc0010I After 74300 nodes, 18569 on tree, 136107.91 best solution, best possible 132202.59 (919.29 seconds)\n", + "Cbc0010I After 74400 nodes, 18572 on tree, 136107.91 best solution, best possible 132202.59 (920.87 seconds)\n", + "Cbc0010I After 74500 nodes, 18566 on tree, 136107.91 best solution, best possible 132202.59 (921.66 seconds)\n", + "Cbc0010I After 74600 nodes, 18576 on tree, 136107.91 best solution, best possible 132202.59 (922.70 seconds)\n", + "Cbc0010I After 74700 nodes, 18571 on tree, 136107.91 best solution, best possible 132202.59 (923.34 seconds)\n", + "Cbc0010I After 74800 nodes, 18572 on tree, 136107.91 best solution, best possible 132202.59 (923.83 seconds)\n", + "Cbc0010I After 74900 nodes, 18569 on tree, 136107.91 best solution, best possible 132202.59 (924.30 seconds)\n", + "Cbc0010I After 75000 nodes, 18566 on tree, 136107.91 best solution, best possible 132202.59 (925.04 seconds)\n", + "Cbc0010I After 75100 nodes, 18582 on tree, 136107.91 best solution, best possible 132202.59 (925.89 seconds)\n", + "Cbc0010I After 75200 nodes, 18574 on tree, 136107.91 best solution, best possible 132202.59 (926.57 seconds)\n", + "Cbc0010I After 75300 nodes, 18572 on tree, 136107.91 best solution, best possible 132202.59 (927.30 seconds)\n", + "Cbc0010I After 75400 nodes, 18568 on tree, 136107.91 best solution, best possible 132202.59 (928.20 seconds)\n", + "Cbc0010I After 75500 nodes, 18567 on tree, 136107.91 best solution, best possible 132202.59 (929.28 seconds)\n", + "Cbc0010I After 75600 nodes, 18568 on tree, 136107.91 best solution, best possible 132202.59 (930.17 seconds)\n", + "Cbc0010I After 75700 nodes, 18565 on tree, 136107.91 best solution, best possible 132202.59 (931.73 seconds)\n", + "Cbc0010I After 75800 nodes, 18571 on tree, 136107.91 best solution, best possible 132202.59 (933.53 seconds)\n", + "Cbc0010I After 75900 nodes, 18564 on tree, 136107.91 best solution, best possible 132202.59 (934.35 seconds)\n", + "Cbc0010I After 76000 nodes, 18564 on tree, 136107.91 best solution, best possible 132202.59 (935.29 seconds)\n", + "Cbc0010I After 76100 nodes, 18553 on tree, 136107.91 best solution, best possible 132202.59 (935.92 seconds)\n", + "Cbc0010I After 76200 nodes, 18525 on tree, 136107.91 best solution, best possible 132202.59 (936.48 seconds)\n", + "Cbc0010I After 76300 nodes, 18501 on tree, 136107.91 best solution, best possible 132202.59 (937.00 seconds)\n", + "Cbc0010I After 76400 nodes, 18473 on tree, 136107.91 best solution, best possible 132202.59 (937.61 seconds)\n", + "Cbc0010I After 76500 nodes, 18472 on tree, 136107.91 best solution, best possible 132202.59 (938.44 seconds)\n", + "Cbc0010I After 76600 nodes, 18467 on tree, 136107.91 best solution, best possible 132202.59 (939.28 seconds)\n", + "Cbc0010I After 76700 nodes, 18442 on tree, 136107.91 best solution, best possible 132202.59 (940.07 seconds)\n", + "Cbc0010I After 76800 nodes, 18431 on tree, 136107.91 best solution, best possible 132202.59 (940.97 seconds)\n", + "Cbc0010I After 76900 nodes, 18430 on tree, 136107.91 best solution, best possible 132202.59 (941.60 seconds)\n", + "Cbc0010I After 77000 nodes, 18419 on tree, 136107.91 best solution, best possible 132202.59 (942.27 seconds)\n", + "Cbc0010I After 77100 nodes, 18422 on tree, 136107.91 best solution, best possible 132202.59 (942.99 seconds)\n", + "Cbc0010I After 77200 nodes, 18413 on tree, 136107.91 best solution, best possible 132202.59 (944.55 seconds)\n", + "Cbc0010I After 77300 nodes, 18407 on tree, 136107.91 best solution, best possible 132202.59 (946.18 seconds)\n", + "Cbc0010I After 77400 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (947.34 seconds)\n", + "Cbc0010I After 77500 nodes, 18388 on tree, 136107.91 best solution, best possible 132202.59 (948.40 seconds)\n", + "Cbc0010I After 77600 nodes, 18390 on tree, 136107.91 best solution, best possible 132202.59 (949.42 seconds)\n", + "Cbc0010I After 77700 nodes, 18387 on tree, 136107.91 best solution, best possible 132202.59 (950.10 seconds)\n", + "Cbc0010I After 77800 nodes, 18390 on tree, 136107.91 best solution, best possible 132202.59 (950.70 seconds)\n", + "Cbc0010I After 77900 nodes, 18388 on tree, 136107.91 best solution, best possible 132202.59 (951.21 seconds)\n", + "Cbc0010I After 78000 nodes, 18386 on tree, 136107.91 best solution, best possible 132202.59 (951.80 seconds)\n", + "Cbc0010I After 78100 nodes, 18386 on tree, 136107.91 best solution, best possible 132202.59 (952.50 seconds)\n", + "Cbc0010I After 78200 nodes, 18387 on tree, 136107.91 best solution, best possible 132202.59 (953.30 seconds)\n", + "Cbc0010I After 78300 nodes, 18382 on tree, 136107.91 best solution, best possible 132202.59 (953.96 seconds)\n", + "Cbc0010I After 78400 nodes, 18381 on tree, 136107.91 best solution, best possible 132202.59 (954.53 seconds)\n", + "Cbc0010I After 78500 nodes, 18383 on tree, 136107.91 best solution, best possible 132202.59 (955.19 seconds)\n", + "Cbc0010I After 78600 nodes, 18389 on tree, 136107.91 best solution, best possible 132202.59 (955.80 seconds)\n", + "Cbc0010I After 78700 nodes, 18387 on tree, 136107.91 best solution, best possible 132202.59 (957.02 seconds)\n", + "Cbc0010I After 78800 nodes, 18384 on tree, 136107.91 best solution, best possible 132202.59 (958.35 seconds)\n", + "Cbc0010I After 78900 nodes, 18384 on tree, 136107.91 best solution, best possible 132202.59 (959.12 seconds)\n", + "Cbc0010I After 79000 nodes, 18384 on tree, 136107.91 best solution, best possible 132202.59 (959.64 seconds)\n", + "Cbc0010I After 79100 nodes, 18386 on tree, 136107.91 best solution, best possible 132202.59 (960.25 seconds)\n", + "Cbc0010I After 79200 nodes, 18389 on tree, 136107.91 best solution, best possible 132202.59 (960.89 seconds)\n", + "Cbc0010I After 79300 nodes, 18381 on tree, 136107.91 best solution, best possible 132202.59 (961.47 seconds)\n", + "Cbc0010I After 79400 nodes, 18384 on tree, 136107.91 best solution, best possible 132202.59 (962.22 seconds)\n", + "Cbc0010I After 79500 nodes, 18395 on tree, 136107.91 best solution, best possible 132202.59 (962.99 seconds)\n", + "Cbc0010I After 79600 nodes, 18388 on tree, 136107.91 best solution, best possible 132202.59 (963.67 seconds)\n", + "Cbc0010I After 79700 nodes, 18392 on tree, 136107.91 best solution, best possible 132202.59 (964.53 seconds)\n", + "Cbc0010I After 79800 nodes, 18388 on tree, 136107.91 best solution, best possible 132202.59 (965.25 seconds)\n", + "Cbc0010I After 79900 nodes, 18385 on tree, 136107.91 best solution, best possible 132202.59 (966.00 seconds)\n", + "Cbc0010I After 80000 nodes, 18385 on tree, 136107.91 best solution, best possible 132202.59 (966.81 seconds)\n", + "Cbc0010I After 80100 nodes, 18390 on tree, 136107.91 best solution, best possible 132202.59 (967.53 seconds)\n", + "Cbc0010I After 80200 nodes, 18389 on tree, 136107.91 best solution, best possible 132202.59 (968.28 seconds)\n", + "Cbc0010I After 80300 nodes, 18388 on tree, 136107.91 best solution, best possible 132202.59 (969.48 seconds)\n", + "Cbc0010I After 80400 nodes, 18392 on tree, 136107.91 best solution, best possible 132202.59 (970.96 seconds)\n", + "Cbc0010I After 80500 nodes, 18382 on tree, 136107.91 best solution, best possible 132202.59 (971.87 seconds)\n", + "Cbc0010I After 80600 nodes, 18391 on tree, 136107.91 best solution, best possible 132202.59 (972.89 seconds)\n", + "Cbc0010I After 80700 nodes, 18396 on tree, 136107.91 best solution, best possible 132202.59 (973.73 seconds)\n", + "Cbc0010I After 80800 nodes, 18394 on tree, 136107.91 best solution, best possible 132202.59 (974.45 seconds)\n", + "Cbc0010I After 80900 nodes, 18391 on tree, 136107.91 best solution, best possible 132202.59 (975.03 seconds)\n", + "Cbc0010I After 81000 nodes, 18385 on tree, 136107.91 best solution, best possible 132202.59 (975.76 seconds)\n", + "Cbc0010I After 81100 nodes, 18384 on tree, 136107.91 best solution, best possible 132202.59 (976.69 seconds)\n", + "Cbc0010I After 81200 nodes, 18398 on tree, 136107.91 best solution, best possible 132202.59 (977.63 seconds)\n", + "Cbc0010I After 81300 nodes, 18394 on tree, 136107.91 best solution, best possible 132202.59 (978.42 seconds)\n", + "Cbc0010I After 81400 nodes, 18391 on tree, 136107.91 best solution, best possible 132202.59 (979.26 seconds)\n", + "Cbc0010I After 81500 nodes, 18391 on tree, 136107.91 best solution, best possible 132202.59 (979.97 seconds)\n", + "Cbc0010I After 81600 nodes, 18384 on tree, 136107.91 best solution, best possible 132202.59 (980.72 seconds)\n", + "Cbc0010I After 81700 nodes, 18387 on tree, 136107.91 best solution, best possible 132202.59 (981.91 seconds)\n", + "Cbc0010I After 81800 nodes, 18384 on tree, 136107.91 best solution, best possible 132202.59 (983.07 seconds)\n", + "Cbc0010I After 81900 nodes, 18383 on tree, 136107.91 best solution, best possible 132202.59 (984.50 seconds)\n", + "Cbc0010I After 82000 nodes, 18388 on tree, 136107.91 best solution, best possible 132202.59 (985.20 seconds)\n", + "Cbc0010I After 82100 nodes, 18386 on tree, 136107.91 best solution, best possible 132202.59 (985.99 seconds)\n", + "Cbc0010I After 82200 nodes, 18387 on tree, 136107.91 best solution, best possible 132202.59 (986.67 seconds)\n", + "Cbc0010I After 82300 nodes, 18384 on tree, 136107.91 best solution, best possible 132202.59 (987.59 seconds)\n", + "Cbc0010I After 82400 nodes, 18387 on tree, 136107.91 best solution, best possible 132202.59 (988.37 seconds)\n", + "Cbc0010I After 82500 nodes, 18397 on tree, 136107.91 best solution, best possible 132202.59 (989.02 seconds)\n", + "Cbc0010I After 82600 nodes, 18386 on tree, 136107.91 best solution, best possible 132202.59 (989.67 seconds)\n", + "Cbc0010I After 82700 nodes, 18385 on tree, 136107.91 best solution, best possible 132202.59 (990.87 seconds)\n", + "Cbc0010I After 82800 nodes, 18384 on tree, 136107.91 best solution, best possible 132202.59 (991.83 seconds)\n", + "Cbc0010I After 82900 nodes, 18386 on tree, 136107.91 best solution, best possible 132202.59 (992.71 seconds)\n", + "Cbc0010I After 83000 nodes, 18388 on tree, 136107.91 best solution, best possible 132202.59 (993.59 seconds)\n", + "Cbc0010I After 83100 nodes, 18390 on tree, 136107.91 best solution, best possible 132202.59 (994.93 seconds)\n", + "Cbc0010I After 83200 nodes, 18385 on tree, 136107.91 best solution, best possible 132202.59 (996.54 seconds)\n", + "Cbc0010I After 83300 nodes, 18386 on tree, 136107.91 best solution, best possible 132202.59 (997.60 seconds)\n", + "Cbc0010I After 83400 nodes, 18387 on tree, 136107.91 best solution, best possible 132202.59 (998.58 seconds)\n", + "Cbc0010I After 83500 nodes, 18393 on tree, 136107.91 best solution, best possible 132202.59 (999.51 seconds)\n", + "Cbc0010I After 83600 nodes, 18383 on tree, 136107.91 best solution, best possible 132202.59 (1000.33 seconds)\n", + "Cbc0010I After 83700 nodes, 18389 on tree, 136107.91 best solution, best possible 132202.59 (1001.22 seconds)\n", + "Cbc0010I After 83800 nodes, 18392 on tree, 136107.91 best solution, best possible 132202.59 (1002.49 seconds)\n", + "Cbc0010I After 83900 nodes, 18392 on tree, 136107.91 best solution, best possible 132202.59 (1003.97 seconds)\n", + "Cbc0010I After 84000 nodes, 18389 on tree, 136107.91 best solution, best possible 132202.59 (1004.83 seconds)\n", + "Cbc0010I After 84100 nodes, 18385 on tree, 136107.91 best solution, best possible 132202.59 (1005.82 seconds)\n", + "Cbc0010I After 84200 nodes, 18383 on tree, 136107.91 best solution, best possible 132202.59 (1006.94 seconds)\n", + "Cbc0010I After 84300 nodes, 18385 on tree, 136107.91 best solution, best possible 132202.59 (1008.76 seconds)\n", + "Cbc0010I After 84400 nodes, 18387 on tree, 136107.91 best solution, best possible 132202.59 (1009.95 seconds)\n", + "Cbc0010I After 84500 nodes, 18383 on tree, 136107.91 best solution, best possible 132202.59 (1011.23 seconds)\n", + "Cbc0010I After 84600 nodes, 18384 on tree, 136107.91 best solution, best possible 132202.59 (1012.27 seconds)\n", + "Cbc0010I After 84700 nodes, 18344 on tree, 136107.91 best solution, best possible 132202.59 (1013.14 seconds)\n", + "Cbc0010I After 84800 nodes, 18322 on tree, 136107.91 best solution, best possible 132202.59 (1013.80 seconds)\n", + "Cbc0010I After 84900 nodes, 18310 on tree, 136107.91 best solution, best possible 132202.59 (1014.42 seconds)\n", + "Cbc0010I After 85000 nodes, 18303 on tree, 136107.91 best solution, best possible 132202.59 (1015.18 seconds)\n", + "Cbc0010I After 85100 nodes, 18278 on tree, 136107.91 best solution, best possible 132202.59 (1015.82 seconds)\n", + "Cbc0010I After 85200 nodes, 18272 on tree, 136107.91 best solution, best possible 132202.59 (1016.69 seconds)\n", + "Cbc0010I After 85300 nodes, 18266 on tree, 136107.91 best solution, best possible 132202.59 (1017.41 seconds)\n", + "Cbc0010I After 85400 nodes, 18255 on tree, 136107.91 best solution, best possible 132202.59 (1018.05 seconds)\n", + "Cbc0010I After 85500 nodes, 18234 on tree, 136107.91 best solution, best possible 132202.59 (1018.80 seconds)\n", + "Cbc0010I After 85600 nodes, 18226 on tree, 136107.91 best solution, best possible 132202.59 (1019.64 seconds)\n", + "Cbc0010I After 85700 nodes, 18222 on tree, 136107.91 best solution, best possible 132202.59 (1020.87 seconds)\n", + "Cbc0010I After 85800 nodes, 18224 on tree, 136107.91 best solution, best possible 132202.59 (1022.17 seconds)\n", + "Cbc0010I After 85900 nodes, 18220 on tree, 136107.91 best solution, best possible 132202.59 (1023.14 seconds)\n", + "Cbc0010I After 86000 nodes, 18212 on tree, 136107.91 best solution, best possible 132202.59 (1023.93 seconds)\n", + "Cbc0010I After 86100 nodes, 18260 on tree, 136107.91 best solution, best possible 132202.59 (1025.51 seconds)\n", + "Cbc0010I After 86200 nodes, 18308 on tree, 136107.91 best solution, best possible 132202.59 (1027.52 seconds)\n", + "Cbc0010I After 86300 nodes, 18357 on tree, 136107.91 best solution, best possible 132202.59 (1029.23 seconds)\n", + "Cbc0010I After 86400 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1030.98 seconds)\n", + "Cbc0010I After 86500 nodes, 18449 on tree, 136107.91 best solution, best possible 132202.59 (1034.13 seconds)\n", + "Cbc0010I After 86600 nodes, 18495 on tree, 136107.91 best solution, best possible 132202.59 (1036.01 seconds)\n", + "Cbc0010I After 86700 nodes, 18538 on tree, 136107.91 best solution, best possible 132202.59 (1037.72 seconds)\n", + "Cbc0010I After 86800 nodes, 18583 on tree, 136107.91 best solution, best possible 132202.59 (1039.19 seconds)\n", + "Cbc0010I After 86900 nodes, 18630 on tree, 136107.91 best solution, best possible 132202.59 (1040.55 seconds)\n", + "Cbc0010I After 87000 nodes, 18669 on tree, 136107.91 best solution, best possible 132202.59 (1041.96 seconds)\n", + "Cbc0010I After 87100 nodes, 18676 on tree, 136107.91 best solution, best possible 132202.59 (1042.80 seconds)\n", + "Cbc0010I After 87200 nodes, 18673 on tree, 136107.91 best solution, best possible 132202.59 (1043.50 seconds)\n", + "Cbc0010I After 87300 nodes, 18667 on tree, 136107.91 best solution, best possible 132202.59 (1044.39 seconds)\n", + "Cbc0010I After 87400 nodes, 18669 on tree, 136107.91 best solution, best possible 132202.59 (1045.04 seconds)\n", + "Cbc0010I After 87500 nodes, 18667 on tree, 136107.91 best solution, best possible 132202.59 (1046.20 seconds)\n", + "Cbc0010I After 87600 nodes, 18656 on tree, 136107.91 best solution, best possible 132202.59 (1047.67 seconds)\n", + "Cbc0010I After 87700 nodes, 18653 on tree, 136107.91 best solution, best possible 132202.59 (1048.33 seconds)\n", + "Cbc0010I After 87800 nodes, 18641 on tree, 136107.91 best solution, best possible 132202.59 (1049.03 seconds)\n", + "Cbc0010I After 87900 nodes, 18623 on tree, 136107.91 best solution, best possible 132202.59 (1049.90 seconds)\n", + "Cbc0010I After 88000 nodes, 18628 on tree, 136107.91 best solution, best possible 132202.59 (1051.40 seconds)\n", + "Cbc0010I After 88100 nodes, 18627 on tree, 136107.91 best solution, best possible 132202.59 (1053.10 seconds)\n", + "Cbc0010I After 88200 nodes, 18622 on tree, 136107.91 best solution, best possible 132202.59 (1054.05 seconds)\n", + "Cbc0010I After 88300 nodes, 18619 on tree, 136107.91 best solution, best possible 132202.59 (1054.69 seconds)\n", + "Cbc0010I After 88400 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1055.51 seconds)\n", + "Cbc0010I After 88500 nodes, 18605 on tree, 136107.91 best solution, best possible 132202.59 (1056.06 seconds)\n", + "Cbc0010I After 88600 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1056.58 seconds)\n", + "Cbc0010I After 88700 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1057.24 seconds)\n", + "Cbc0010I After 88800 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1057.96 seconds)\n", + "Cbc0010I After 88900 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1059.09 seconds)\n", + "Cbc0010I After 89000 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1060.26 seconds)\n", + "Cbc0010I After 89100 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1061.12 seconds)\n", + "Cbc0010I After 89200 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1061.66 seconds)\n", + "Cbc0010I After 89300 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1062.33 seconds)\n", + "Cbc0010I After 89400 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1063.04 seconds)\n", + "Cbc0010I After 89500 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1063.72 seconds)\n", + "Cbc0010I After 89600 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1064.39 seconds)\n", + "Cbc0010I After 89700 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1065.11 seconds)\n", + "Cbc0010I After 89800 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1065.65 seconds)\n", + "Cbc0010I After 89900 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1066.19 seconds)\n", + "Cbc0010I After 90000 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1067.01 seconds)\n", + "Cbc0010I After 90100 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1067.81 seconds)\n", + "Cbc0010I After 90200 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1068.45 seconds)\n", + "Cbc0010I After 90300 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1069.38 seconds)\n", + "Cbc0010I After 90400 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1070.32 seconds)\n", + "Cbc0010I After 90500 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1071.37 seconds)\n", + "Cbc0010I After 90600 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1072.69 seconds)\n", + "Cbc0010I After 90700 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1073.88 seconds)\n", + "Cbc0010I After 90800 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1074.62 seconds)\n", + "Cbc0010I After 90900 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1075.33 seconds)\n", + "Cbc0010I After 91000 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1075.85 seconds)\n", + "Cbc0010I After 91100 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1076.66 seconds)\n", + "Cbc0010I After 91200 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1077.40 seconds)\n", + "Cbc0010I After 91300 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1078.25 seconds)\n", + "Cbc0010I After 91400 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1078.87 seconds)\n", + "Cbc0010I After 91500 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1079.54 seconds)\n", + "Cbc0010I After 91600 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1080.15 seconds)\n", + "Cbc0010I After 91700 nodes, 18620 on tree, 136107.91 best solution, best possible 132202.59 (1080.80 seconds)\n", + "Cbc0010I After 91800 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1081.51 seconds)\n", + "Cbc0010I After 91900 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1082.26 seconds)\n", + "Cbc0010I After 92000 nodes, 18605 on tree, 136107.91 best solution, best possible 132202.59 (1083.17 seconds)\n", + "Cbc0010I After 92100 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1084.12 seconds)\n", + "Cbc0010I After 92200 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1085.33 seconds)\n", + "Cbc0010I After 92300 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1086.51 seconds)\n", + "Cbc0010I After 92400 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1087.34 seconds)\n", + "Cbc0010I After 92500 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1088.15 seconds)\n", + "Cbc0010I After 92600 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1088.81 seconds)\n", + "Cbc0010I After 92700 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1089.65 seconds)\n", + "Cbc0010I After 92800 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1090.49 seconds)\n", + "Cbc0010I After 92900 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1091.19 seconds)\n", + "Cbc0010I After 93000 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1091.91 seconds)\n", + "Cbc0010I After 93100 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1092.68 seconds)\n", + "Cbc0010I After 93200 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1093.34 seconds)\n", + "Cbc0010I After 93300 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1093.98 seconds)\n", + "Cbc0010I After 93400 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1094.71 seconds)\n", + "Cbc0010I After 93500 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1095.57 seconds)\n", + "Cbc0010I After 93600 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1096.45 seconds)\n", + "Cbc0010I After 93700 nodes, 18618 on tree, 136107.91 best solution, best possible 132202.59 (1097.65 seconds)\n", + "Cbc0010I After 93800 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1099.01 seconds)\n", + "Cbc0010I After 93900 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1099.98 seconds)\n", + "Cbc0010I After 94000 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1100.68 seconds)\n", + "Cbc0010I After 94100 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1101.85 seconds)\n", + "Cbc0010I After 94200 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1102.69 seconds)\n", + "Cbc0010I After 94300 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1103.48 seconds)\n", + "Cbc0010I After 94400 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1104.24 seconds)\n", + "Cbc0010I After 94500 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1105.01 seconds)\n", + "Cbc0010I After 94600 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1105.85 seconds)\n", + "Cbc0010I After 94700 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1106.78 seconds)\n", + "Cbc0010I After 94800 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1107.61 seconds)\n", + "Cbc0010I After 94900 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1108.38 seconds)\n", + "Cbc0010I After 95000 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1109.27 seconds)\n", + "Cbc0010I After 95100 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1110.74 seconds)\n", + "Cbc0010I After 95200 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1112.10 seconds)\n", + "Cbc0010I After 95300 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1113.05 seconds)\n", + "Cbc0010I After 95400 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1114.06 seconds)\n", + "Cbc0010I After 95500 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1114.97 seconds)\n", + "Cbc0010I After 95600 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1116.24 seconds)\n", + "Cbc0010I After 95700 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1117.32 seconds)\n", + "Cbc0010I After 95800 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1118.48 seconds)\n", + "Cbc0010I After 95900 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1119.26 seconds)\n", + "Cbc0010I After 96000 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1120.10 seconds)\n", + "Cbc0010I After 96100 nodes, 18605 on tree, 136107.91 best solution, best possible 132202.59 (1120.96 seconds)\n", + "Cbc0010I After 96200 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1121.76 seconds)\n", + "Cbc0010I After 96300 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1122.82 seconds)\n", + "Cbc0010I After 96400 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1124.65 seconds)\n", + "Cbc0010I After 96500 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1125.38 seconds)\n", + "Cbc0010I After 96600 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1126.34 seconds)\n", + "Cbc0010I After 96700 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1127.03 seconds)\n", + "Cbc0010I After 96800 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1127.80 seconds)\n", + "Cbc0010I After 96900 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1128.85 seconds)\n", + "Cbc0010I After 97000 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1130.10 seconds)\n", + "Cbc0010I After 97100 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1131.28 seconds)\n", + "Cbc0010I After 97200 nodes, 18623 on tree, 136107.91 best solution, best possible 132202.59 (1132.21 seconds)\n", + "Cbc0010I After 97300 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1132.92 seconds)\n", + "Cbc0010I After 97400 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1133.89 seconds)\n", + "Cbc0010I After 97500 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1134.62 seconds)\n", + "Cbc0010I After 97600 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1136.44 seconds)\n", + "Cbc0010I After 97700 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1137.66 seconds)\n", + "Cbc0010I After 97800 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1138.60 seconds)\n", + "Cbc0010I After 97900 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1139.59 seconds)\n", + "Cbc0010I After 98000 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1140.46 seconds)\n", + "Cbc0010I After 98100 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1141.31 seconds)\n", + "Cbc0010I After 98200 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1142.27 seconds)\n", + "Cbc0010I After 98300 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1143.27 seconds)\n", + "Cbc0010I After 98400 nodes, 18605 on tree, 136107.91 best solution, best possible 132202.59 (1144.53 seconds)\n", + "Cbc0010I After 98500 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1145.42 seconds)\n", + "Cbc0010I After 98600 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1146.37 seconds)\n", + "Cbc0010I After 98700 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1147.49 seconds)\n", + "Cbc0010I After 98800 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1148.82 seconds)\n", + "Cbc0010I After 98900 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1150.60 seconds)\n", + "Cbc0010I After 99000 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1151.37 seconds)\n", + "Cbc0010I After 99100 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1152.42 seconds)\n", + "Cbc0010I After 99200 nodes, 18618 on tree, 136107.91 best solution, best possible 132202.59 (1153.27 seconds)\n", + "Cbc0010I After 99300 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1154.04 seconds)\n", + "Cbc0010I After 99400 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1155.18 seconds)\n", + "Cbc0010I After 99500 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1156.78 seconds)\n", + "Cbc0010I After 99600 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1157.90 seconds)\n", + "Cbc0010I After 99700 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1159.20 seconds)\n", + "Cbc0010I After 99800 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1160.24 seconds)\n", + "Cbc0010I After 99900 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1162.37 seconds)\n", + "Cbc0010I After 100000 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1163.53 seconds)\n", + "Cbc0010I After 100100 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1164.40 seconds)\n", + "Cbc0010I After 100200 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1165.33 seconds)\n", + "Cbc0010I After 100300 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1166.36 seconds)\n", + "Cbc0010I After 100400 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1167.23 seconds)\n", + "Cbc0010I After 100500 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1168.13 seconds)\n", + "Cbc0010I After 100600 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1169.24 seconds)\n", + "Cbc0010I After 100700 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1170.20 seconds)\n", + "Cbc0010I After 100800 nodes, 18619 on tree, 136107.91 best solution, best possible 132202.59 (1171.01 seconds)\n", + "Cbc0010I After 100900 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1171.84 seconds)\n", + "Cbc0010I After 101000 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1172.61 seconds)\n", + "Cbc0010I After 101100 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1173.72 seconds)\n", + "Cbc0010I After 101200 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1175.03 seconds)\n", + "Cbc0010I After 101300 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1176.37 seconds)\n", + "Cbc0010I After 101400 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1177.21 seconds)\n", + "Cbc0010I After 101500 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1178.18 seconds)\n", + "Cbc0010I After 101600 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1179.54 seconds)\n", + "Cbc0010I After 101700 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1180.68 seconds)\n", + "Cbc0010I After 101800 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1181.91 seconds)\n", + "Cbc0010I After 101900 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1182.95 seconds)\n", + "Cbc0010I After 102000 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1183.97 seconds)\n", + "Cbc0010I After 102100 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1185.01 seconds)\n", + "Cbc0010I After 102200 nodes, 18622 on tree, 136107.91 best solution, best possible 132202.59 (1186.32 seconds)\n", + "Cbc0010I After 102300 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1188.02 seconds)\n", + "Cbc0010I After 102400 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1189.10 seconds)\n", + "Cbc0010I After 102500 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1190.09 seconds)\n", + "Cbc0010I After 102600 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1191.25 seconds)\n", + "Cbc0010I After 102700 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1192.04 seconds)\n", + "Cbc0010I After 102800 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1192.91 seconds)\n", + "Cbc0010I After 102900 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1194.19 seconds)\n", + "Cbc0010I After 103000 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1195.26 seconds)\n", + "Cbc0010I After 103100 nodes, 18618 on tree, 136107.91 best solution, best possible 132202.59 (1196.46 seconds)\n", + "Cbc0010I After 103200 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1197.24 seconds)\n", + "Cbc0010I After 103300 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1198.01 seconds)\n", + "Cbc0010I After 103400 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1198.85 seconds)\n", + "Cbc0010I After 103500 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1200.32 seconds)\n", + "Cbc0010I After 103600 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1201.73 seconds)\n", + "Cbc0010I After 103700 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1202.64 seconds)\n", + "Cbc0010I After 103800 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1203.98 seconds)\n", + "Cbc0010I After 103900 nodes, 18605 on tree, 136107.91 best solution, best possible 132202.59 (1205.09 seconds)\n", + "Cbc0010I After 104000 nodes, 18617 on tree, 136107.91 best solution, best possible 132202.59 (1206.41 seconds)\n", + "Cbc0010I After 104100 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1207.57 seconds)\n", + "Cbc0010I After 104200 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1208.99 seconds)\n", + "Cbc0010I After 104300 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1210.23 seconds)\n", + "Cbc0010I After 104400 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1211.57 seconds)\n", + "Cbc0010I After 104500 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1213.40 seconds)\n", + "Cbc0010I After 104600 nodes, 18624 on tree, 136107.91 best solution, best possible 132202.59 (1215.28 seconds)\n", + "Cbc0010I After 104700 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1216.43 seconds)\n", + "Cbc0010I After 104800 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1217.48 seconds)\n", + "Cbc0010I After 104900 nodes, 18618 on tree, 136107.91 best solution, best possible 132202.59 (1218.57 seconds)\n", + "Cbc0010I After 105000 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1219.69 seconds)\n", + "Cbc0010I After 105100 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1221.02 seconds)\n", + "Cbc0010I After 105200 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1222.07 seconds)\n", + "Cbc0010I After 105300 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1223.43 seconds)\n", + "Cbc0010I After 105400 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1224.72 seconds)\n", + "Cbc0010I After 105500 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1226.81 seconds)\n", + "Cbc0010I After 105600 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1228.17 seconds)\n", + "Cbc0010I After 105700 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1229.20 seconds)\n", + "Cbc0010I After 105800 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1230.33 seconds)\n", + "Cbc0010I After 105900 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1231.39 seconds)\n", + "Cbc0010I After 106000 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1232.37 seconds)\n", + "Cbc0010I After 106100 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1233.52 seconds)\n", + "Cbc0010I After 106200 nodes, 18617 on tree, 136107.91 best solution, best possible 132202.59 (1234.82 seconds)\n", + "Cbc0010I After 106300 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1235.74 seconds)\n", + "Cbc0010I After 106400 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1236.69 seconds)\n", + "Cbc0010I After 106500 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1237.89 seconds)\n", + "Cbc0010I After 106600 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1240.26 seconds)\n", + "Cbc0010I After 106700 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1241.22 seconds)\n", + "Cbc0010I After 106800 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1242.32 seconds)\n", + "Cbc0010I After 106900 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1243.22 seconds)\n", + "Cbc0010I After 107000 nodes, 18605 on tree, 136107.91 best solution, best possible 132202.59 (1244.27 seconds)\n", + "Cbc0010I After 107100 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1245.52 seconds)\n", + "Cbc0010I After 107200 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1246.56 seconds)\n", + "Cbc0010I After 107300 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1247.76 seconds)\n", + "Cbc0010I After 107400 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1248.79 seconds)\n", + "Cbc0010I After 107500 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1249.94 seconds)\n", + "Cbc0010I After 107600 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1251.89 seconds)\n", + "Cbc0010I After 107700 nodes, 18606 on tree, 136107.91 best solution, best possible 132202.59 (1253.24 seconds)\n", + "Cbc0010I After 107800 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1254.22 seconds)\n", + "Cbc0010I After 107900 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1255.30 seconds)\n", + "Cbc0010I After 108000 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1256.56 seconds)\n", + "Cbc0010I After 108100 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1257.56 seconds)\n", + "Cbc0010I After 108200 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1258.62 seconds)\n", + "Cbc0010I After 108300 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1259.53 seconds)\n", + "Cbc0010I After 108400 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1260.82 seconds)\n", + "Cbc0010I After 108500 nodes, 18611 on tree, 136107.91 best solution, best possible 132202.59 (1261.95 seconds)\n", + "Cbc0010I After 108600 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1262.95 seconds)\n", + "Cbc0010I After 108700 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1264.46 seconds)\n", + "Cbc0010I After 108800 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1265.96 seconds)\n", + "Cbc0010I After 108900 nodes, 18610 on tree, 136107.91 best solution, best possible 132202.59 (1266.61 seconds)\n", + "Cbc0010I After 109000 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1267.49 seconds)\n", + "Cbc0010I After 109100 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1268.09 seconds)\n", + "Cbc0010I After 109200 nodes, 18617 on tree, 136107.91 best solution, best possible 132202.59 (1269.21 seconds)\n", + "Cbc0010I After 109300 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1270.34 seconds)\n", + "Cbc0010I After 109400 nodes, 18613 on tree, 136107.91 best solution, best possible 132202.59 (1271.47 seconds)\n", + "Cbc0010I After 109500 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1272.49 seconds)\n", + "Cbc0010I After 109600 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1273.48 seconds)\n", + "Cbc0010I After 109700 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1274.29 seconds)\n", + "Cbc0010I After 109800 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1275.35 seconds)\n", + "Cbc0010I After 109900 nodes, 18624 on tree, 136107.91 best solution, best possible 132202.59 (1276.99 seconds)\n", + "Cbc0010I After 110000 nodes, 18627 on tree, 136107.91 best solution, best possible 132202.59 (1278.70 seconds)\n", + "Cbc0010I After 110100 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1279.85 seconds)\n", + "Cbc0010I After 110200 nodes, 18614 on tree, 136107.91 best solution, best possible 132202.59 (1280.88 seconds)\n", + "Cbc0010I After 110300 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1281.86 seconds)\n", + "Cbc0010I After 110400 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1283.02 seconds)\n", + "Cbc0010I After 110500 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1284.08 seconds)\n", + "Cbc0010I After 110600 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1284.98 seconds)\n", + "Cbc0010I After 110700 nodes, 18612 on tree, 136107.91 best solution, best possible 132202.59 (1285.89 seconds)\n", + "Cbc0010I After 110800 nodes, 18616 on tree, 136107.91 best solution, best possible 132202.59 (1287.02 seconds)\n", + "Cbc0010I After 110900 nodes, 18619 on tree, 136107.91 best solution, best possible 132202.59 (1288.17 seconds)\n", + "Cbc0010I After 111000 nodes, 18618 on tree, 136107.91 best solution, best possible 132202.59 (1288.95 seconds)\n", + "Cbc0010I After 111100 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1290.43 seconds)\n", + "Cbc0010I After 111200 nodes, 18609 on tree, 136107.91 best solution, best possible 132202.59 (1291.74 seconds)\n", + "Cbc0010I After 111300 nodes, 18619 on tree, 136107.91 best solution, best possible 132202.59 (1292.83 seconds)\n", + "Cbc0010I After 111400 nodes, 18608 on tree, 136107.91 best solution, best possible 132202.59 (1293.72 seconds)\n", + "Cbc0010I After 111500 nodes, 18584 on tree, 136107.91 best solution, best possible 132202.59 (1294.56 seconds)\n", + "Cbc0010I After 111600 nodes, 18560 on tree, 136107.91 best solution, best possible 132202.59 (1295.38 seconds)\n", + "Cbc0010I After 111700 nodes, 18547 on tree, 136107.91 best solution, best possible 132202.59 (1296.29 seconds)\n", + "Cbc0010I After 111800 nodes, 18523 on tree, 136107.91 best solution, best possible 132202.59 (1297.00 seconds)\n", + "Cbc0010I After 111900 nodes, 18496 on tree, 136107.91 best solution, best possible 132202.59 (1297.76 seconds)\n", + "Cbc0010I After 112000 nodes, 18489 on tree, 136107.91 best solution, best possible 132202.59 (1298.65 seconds)\n", + "Cbc0010I After 112100 nodes, 18470 on tree, 136107.91 best solution, best possible 132202.59 (1299.67 seconds)\n", + "Cbc0010I After 112200 nodes, 18452 on tree, 136107.91 best solution, best possible 132202.59 (1300.42 seconds)\n", + "Cbc0010I After 112300 nodes, 18428 on tree, 136107.91 best solution, best possible 132202.59 (1301.18 seconds)\n", + "Cbc0010I After 112400 nodes, 18410 on tree, 136107.91 best solution, best possible 132202.59 (1302.03 seconds)\n", + "Cbc0010I After 112500 nodes, 18398 on tree, 136107.91 best solution, best possible 132202.59 (1303.54 seconds)\n", + "Cbc0010I After 112600 nodes, 18379 on tree, 136107.91 best solution, best possible 132202.59 (1304.81 seconds)\n", + "Cbc0010I After 112700 nodes, 18371 on tree, 136107.91 best solution, best possible 132202.59 (1305.59 seconds)\n", + "Cbc0010I After 112800 nodes, 18365 on tree, 136107.91 best solution, best possible 132202.59 (1306.36 seconds)\n", + "Cbc0010I After 112900 nodes, 18349 on tree, 136107.91 best solution, best possible 132202.59 (1307.32 seconds)\n", + "Cbc0010I After 113000 nodes, 18327 on tree, 136107.91 best solution, best possible 132202.59 (1308.17 seconds)\n", + "Cbc0010I After 113100 nodes, 18306 on tree, 136107.91 best solution, best possible 132202.59 (1309.11 seconds)\n", + "Cbc0010I After 113200 nodes, 18288 on tree, 136107.91 best solution, best possible 132202.59 (1310.14 seconds)\n", + "Cbc0010I After 113300 nodes, 18272 on tree, 136107.91 best solution, best possible 132202.59 (1311.07 seconds)\n", + "Cbc0010I After 113400 nodes, 18249 on tree, 136107.91 best solution, best possible 132202.59 (1311.96 seconds)\n", + "Cbc0010I After 113500 nodes, 18238 on tree, 136107.91 best solution, best possible 132202.59 (1313.16 seconds)\n", + "Cbc0010I After 113600 nodes, 18222 on tree, 136107.91 best solution, best possible 132202.59 (1314.10 seconds)\n", + "Cbc0010I After 113700 nodes, 18225 on tree, 136107.91 best solution, best possible 132202.59 (1314.99 seconds)\n", + "Cbc0010I After 113800 nodes, 18219 on tree, 136107.91 best solution, best possible 132202.59 (1316.24 seconds)\n", + "Cbc0010I After 113900 nodes, 18211 on tree, 136107.91 best solution, best possible 132202.59 (1317.69 seconds)\n", + "Cbc0010I After 114000 nodes, 18202 on tree, 136107.91 best solution, best possible 132202.59 (1318.72 seconds)\n", + "Cbc0010I After 114100 nodes, 18246 on tree, 136107.91 best solution, best possible 132202.59 (1320.67 seconds)\n", + "Cbc0010I After 114200 nodes, 18287 on tree, 136107.91 best solution, best possible 132202.59 (1322.53 seconds)\n", + "Cbc0010I After 114300 nodes, 18332 on tree, 136107.91 best solution, best possible 132202.59 (1324.31 seconds)\n", + "Cbc0010I After 114400 nodes, 18380 on tree, 136107.91 best solution, best possible 132202.59 (1326.06 seconds)\n", + "Cbc0010I After 114500 nodes, 18422 on tree, 136107.91 best solution, best possible 132202.59 (1328.81 seconds)\n", + "Cbc0010I After 114600 nodes, 18464 on tree, 136107.91 best solution, best possible 132202.59 (1331.09 seconds)\n", + "Cbc0010I After 114700 nodes, 18509 on tree, 136107.91 best solution, best possible 132202.59 (1333.10 seconds)\n", + "Cbc0010I After 114800 nodes, 18550 on tree, 136107.91 best solution, best possible 132202.59 (1334.89 seconds)\n", + "Cbc0010I After 114900 nodes, 18594 on tree, 136107.91 best solution, best possible 132202.59 (1336.70 seconds)\n", + "Cbc0010I After 115000 nodes, 18638 on tree, 136107.91 best solution, best possible 132202.59 (1338.39 seconds)\n", + "Cbc0010I After 115100 nodes, 18627 on tree, 136107.91 best solution, best possible 132202.59 (1339.10 seconds)\n", + "Cbc0010I After 115200 nodes, 18615 on tree, 136107.91 best solution, best possible 132202.59 (1339.97 seconds)\n", + "Cbc0010I After 115300 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1341.72 seconds)\n", + "Cbc0010I After 115400 nodes, 18607 on tree, 136107.91 best solution, best possible 132202.59 (1342.97 seconds)\n", + "Cbc0010I After 115500 nodes, 18597 on tree, 136107.91 best solution, best possible 132202.59 (1343.80 seconds)\n", + "Cbc0010I After 115600 nodes, 18593 on tree, 136107.91 best solution, best possible 132202.59 (1344.83 seconds)\n", + "Cbc0010I After 115700 nodes, 18593 on tree, 136107.91 best solution, best possible 132202.59 (1345.79 seconds)\n", + "Cbc0010I After 115800 nodes, 18588 on tree, 136107.91 best solution, best possible 132202.59 (1346.82 seconds)\n", + "Cbc0010I After 115900 nodes, 18577 on tree, 136107.91 best solution, best possible 132202.59 (1347.88 seconds)\n", + "Cbc0010I After 116000 nodes, 18579 on tree, 136107.91 best solution, best possible 132202.59 (1348.84 seconds)\n", + "Cbc0010I After 116100 nodes, 18558 on tree, 136107.91 best solution, best possible 132202.59 (1349.80 seconds)\n", + "Cbc0010I After 116200 nodes, 18548 on tree, 136107.91 best solution, best possible 132202.59 (1350.62 seconds)\n", + "Cbc0010I After 116300 nodes, 18546 on tree, 136107.91 best solution, best possible 132202.59 (1351.50 seconds)\n", + "Cbc0010I After 116400 nodes, 18532 on tree, 136107.91 best solution, best possible 132202.59 (1352.33 seconds)\n", + "Cbc0010I After 116500 nodes, 18518 on tree, 136107.91 best solution, best possible 132202.59 (1353.72 seconds)\n", + "Cbc0010I After 116600 nodes, 18511 on tree, 136107.91 best solution, best possible 132202.59 (1355.47 seconds)\n", + "Cbc0010I After 116700 nodes, 18518 on tree, 136107.91 best solution, best possible 132202.59 (1356.36 seconds)\n", + "Cbc0010I After 116800 nodes, 18514 on tree, 136107.91 best solution, best possible 132202.59 (1357.13 seconds)\n", + "Cbc0010I After 116900 nodes, 18509 on tree, 136107.91 best solution, best possible 132202.59 (1358.00 seconds)\n", + "Cbc0010I After 117000 nodes, 18501 on tree, 136107.91 best solution, best possible 132202.59 (1359.06 seconds)\n", + "Cbc0010I After 117100 nodes, 18490 on tree, 136107.91 best solution, best possible 132202.59 (1360.08 seconds)\n", + "Cbc0010I After 117200 nodes, 18484 on tree, 136107.91 best solution, best possible 132202.59 (1361.18 seconds)\n", + "Cbc0010I After 117300 nodes, 18484 on tree, 136107.91 best solution, best possible 132202.59 (1362.23 seconds)\n", + "Cbc0010I After 117400 nodes, 18480 on tree, 136107.91 best solution, best possible 132202.59 (1363.45 seconds)\n", + "Cbc0010I After 117500 nodes, 18485 on tree, 136107.91 best solution, best possible 132202.59 (1364.48 seconds)\n", + "Cbc0010I After 117600 nodes, 18476 on tree, 136107.91 best solution, best possible 132202.59 (1365.66 seconds)\n", + "Cbc0010I After 117700 nodes, 18469 on tree, 136107.91 best solution, best possible 132202.59 (1367.45 seconds)\n", + "Cbc0010I After 117800 nodes, 18468 on tree, 136107.91 best solution, best possible 132202.59 (1368.99 seconds)\n", + "Cbc0010I After 117900 nodes, 18459 on tree, 136107.91 best solution, best possible 132202.59 (1370.02 seconds)\n", + "Cbc0010I After 118000 nodes, 18452 on tree, 136107.91 best solution, best possible 132202.59 (1370.79 seconds)\n", + "Cbc0010I After 118100 nodes, 18443 on tree, 136107.91 best solution, best possible 132202.59 (1371.81 seconds)\n", + "Cbc0010I After 118200 nodes, 18436 on tree, 136107.91 best solution, best possible 132202.59 (1372.72 seconds)\n", + "Cbc0010I After 118300 nodes, 18433 on tree, 136107.91 best solution, best possible 132202.59 (1373.89 seconds)\n", + "Cbc0010I After 118400 nodes, 18432 on tree, 136107.91 best solution, best possible 132202.59 (1374.95 seconds)\n", + "Cbc0010I After 118500 nodes, 18425 on tree, 136107.91 best solution, best possible 132202.59 (1375.72 seconds)\n", + "Cbc0010I After 118600 nodes, 18422 on tree, 136107.91 best solution, best possible 132202.59 (1376.96 seconds)\n", + "Cbc0010I After 118700 nodes, 18427 on tree, 136107.91 best solution, best possible 132202.59 (1378.07 seconds)\n", + "Cbc0010I After 118800 nodes, 18427 on tree, 136107.91 best solution, best possible 132202.59 (1379.89 seconds)\n", + "Cbc0010I After 118900 nodes, 18419 on tree, 136107.91 best solution, best possible 132202.59 (1381.21 seconds)\n", + "Cbc0010I After 119000 nodes, 18421 on tree, 136107.91 best solution, best possible 132202.59 (1382.63 seconds)\n", + "Cbc0010I After 119100 nodes, 18419 on tree, 136107.91 best solution, best possible 132202.59 (1383.65 seconds)\n", + "Cbc0010I After 119200 nodes, 18412 on tree, 136107.91 best solution, best possible 132202.59 (1384.66 seconds)\n", + "Cbc0010I After 119300 nodes, 18408 on tree, 136107.91 best solution, best possible 132202.59 (1385.59 seconds)\n", + "Cbc0010I After 119400 nodes, 18416 on tree, 136107.91 best solution, best possible 132202.59 (1386.76 seconds)\n", + "Cbc0010I After 119500 nodes, 18410 on tree, 136107.91 best solution, best possible 132202.59 (1387.89 seconds)\n", + "Cbc0010I After 119600 nodes, 18416 on tree, 136107.91 best solution, best possible 132202.59 (1389.11 seconds)\n", + "Cbc0010I After 119700 nodes, 18415 on tree, 136107.91 best solution, best possible 132202.59 (1390.26 seconds)\n", + "Cbc0010I After 119800 nodes, 18409 on tree, 136107.91 best solution, best possible 132202.59 (1391.67 seconds)\n", + "Cbc0010I After 119900 nodes, 18405 on tree, 136107.91 best solution, best possible 132202.59 (1393.39 seconds)\n", + "Cbc0010I After 120000 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1394.65 seconds)\n", + "Cbc0010I After 120100 nodes, 18403 on tree, 136107.91 best solution, best possible 132202.59 (1395.53 seconds)\n", + "Cbc0010I After 120200 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1396.50 seconds)\n", + "Cbc0010I After 120300 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1397.53 seconds)\n", + "Cbc0010I After 120400 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1398.33 seconds)\n", + "Cbc0010I After 120500 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1399.19 seconds)\n", + "Cbc0010I After 120600 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1400.28 seconds)\n", + "Cbc0010I After 120700 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1401.22 seconds)\n", + "Cbc0010I After 120800 nodes, 18406 on tree, 136107.91 best solution, best possible 132202.59 (1402.28 seconds)\n", + "Cbc0010I After 120900 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1403.29 seconds)\n", + "Cbc0010I After 121000 nodes, 18403 on tree, 136107.91 best solution, best possible 132202.59 (1404.91 seconds)\n", + "Cbc0010I After 121100 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1406.93 seconds)\n", + "Cbc0010I After 121200 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1407.73 seconds)\n", + "Cbc0010I After 121300 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1408.72 seconds)\n", + "Cbc0010I After 121400 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1410.05 seconds)\n", + "Cbc0010I After 121500 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1411.48 seconds)\n", + "Cbc0010I After 121600 nodes, 18412 on tree, 136107.91 best solution, best possible 132202.59 (1412.56 seconds)\n", + "Cbc0010I After 121700 nodes, 18407 on tree, 136107.91 best solution, best possible 132202.59 (1413.51 seconds)\n", + "Cbc0010I After 121800 nodes, 18408 on tree, 136107.91 best solution, best possible 132202.59 (1414.39 seconds)\n", + "Cbc0010I After 121900 nodes, 18408 on tree, 136107.91 best solution, best possible 132202.59 (1415.10 seconds)\n", + "Cbc0010I After 122000 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1415.94 seconds)\n", + "Cbc0010I After 122100 nodes, 18408 on tree, 136107.91 best solution, best possible 132202.59 (1416.96 seconds)\n", + "Cbc0010I After 122200 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1419.60 seconds)\n", + "Cbc0010I After 122300 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1420.86 seconds)\n", + "Cbc0010I After 122400 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1422.37 seconds)\n", + "Cbc0010I After 122500 nodes, 18405 on tree, 136107.91 best solution, best possible 132202.59 (1423.79 seconds)\n", + "Cbc0010I After 122600 nodes, 18406 on tree, 136107.91 best solution, best possible 132202.59 (1425.32 seconds)\n", + "Cbc0010I After 122700 nodes, 18398 on tree, 136107.91 best solution, best possible 132202.59 (1426.78 seconds)\n", + "Cbc0010I After 122800 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1427.93 seconds)\n", + "Cbc0010I After 122900 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1429.27 seconds)\n", + "Cbc0010I After 123000 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1431.03 seconds)\n", + "Cbc0010I After 123100 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1433.03 seconds)\n", + "Cbc0010I After 123200 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1433.99 seconds)\n", + "Cbc0010I After 123300 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1435.32 seconds)\n", + "Cbc0010I After 123400 nodes, 18405 on tree, 136107.91 best solution, best possible 132202.59 (1436.21 seconds)\n", + "Cbc0010I After 123500 nodes, 18398 on tree, 136107.91 best solution, best possible 132202.59 (1437.22 seconds)\n", + "Cbc0010I After 123600 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1438.27 seconds)\n", + "Cbc0010I After 123700 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1439.47 seconds)\n", + "Cbc0010I After 123800 nodes, 18405 on tree, 136107.91 best solution, best possible 132202.59 (1440.51 seconds)\n", + "Cbc0010I After 123900 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1441.65 seconds)\n", + "Cbc0010I After 124000 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1443.68 seconds)\n", + "Cbc0010I After 124100 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1446.10 seconds)\n", + "Cbc0010I After 124200 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1447.67 seconds)\n", + "Cbc0010I After 124300 nodes, 18410 on tree, 136107.91 best solution, best possible 132202.59 (1448.75 seconds)\n", + "Cbc0010I After 124400 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1450.01 seconds)\n", + "Cbc0010I After 124500 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1451.10 seconds)\n", + "Cbc0010I After 124600 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1452.26 seconds)\n", + "Cbc0010I After 124700 nodes, 18406 on tree, 136107.91 best solution, best possible 132202.59 (1453.92 seconds)\n", + "Cbc0010I After 124800 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1455.09 seconds)\n", + "Cbc0010I After 124900 nodes, 18403 on tree, 136107.91 best solution, best possible 132202.59 (1457.11 seconds)\n", + "Cbc0010I After 125000 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1458.91 seconds)\n", + "Cbc0010I After 125100 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1460.38 seconds)\n", + "Cbc0010I After 125200 nodes, 18403 on tree, 136107.91 best solution, best possible 132202.59 (1461.64 seconds)\n", + "Cbc0010I After 125300 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1462.70 seconds)\n", + "Cbc0010I After 125400 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1464.03 seconds)\n", + "Cbc0010I After 125500 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1464.99 seconds)\n", + "Cbc0010I After 125600 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1466.15 seconds)\n", + "Cbc0010I After 125700 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1467.16 seconds)\n", + "Cbc0010I After 125800 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1468.53 seconds)\n", + "Cbc0010I After 125900 nodes, 18403 on tree, 136107.91 best solution, best possible 132202.59 (1471.24 seconds)\n", + "Cbc0010I After 126000 nodes, 18407 on tree, 136107.91 best solution, best possible 132202.59 (1473.12 seconds)\n", + "Cbc0010I After 126100 nodes, 18410 on tree, 136107.91 best solution, best possible 132202.59 (1474.34 seconds)\n", + "Cbc0010I After 126200 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1475.07 seconds)\n", + "Cbc0010I After 126300 nodes, 18403 on tree, 136107.91 best solution, best possible 132202.59 (1476.58 seconds)\n", + "Cbc0010I After 126400 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1477.61 seconds)\n", + "Cbc0010I After 126500 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1478.42 seconds)\n", + "Cbc0010I After 126600 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1479.20 seconds)\n", + "Cbc0010I After 126700 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1479.91 seconds)\n", + "Cbc0010I After 126800 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1481.04 seconds)\n", + "Cbc0010I After 126900 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1483.19 seconds)\n", + "Cbc0010I After 127000 nodes, 18403 on tree, 136107.91 best solution, best possible 132202.59 (1484.94 seconds)\n", + "Cbc0010I After 127100 nodes, 18406 on tree, 136107.91 best solution, best possible 132202.59 (1485.78 seconds)\n", + "Cbc0010I After 127200 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1486.75 seconds)\n", + "Cbc0010I After 127300 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1488.04 seconds)\n", + "Cbc0010I After 127400 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1489.19 seconds)\n", + "Cbc0010I After 127500 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1490.63 seconds)\n", + "Cbc0010I After 127600 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1491.97 seconds)\n", + "Cbc0010I After 127700 nodes, 18398 on tree, 136107.91 best solution, best possible 132202.59 (1493.38 seconds)\n", + "Cbc0010I After 127800 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1494.77 seconds)\n", + "Cbc0010I After 127900 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1496.99 seconds)\n", + "Cbc0010I After 128000 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1498.29 seconds)\n", + "Cbc0010I After 128100 nodes, 18402 on tree, 136107.91 best solution, best possible 132202.59 (1499.66 seconds)\n", + "Cbc0010I After 128200 nodes, 18403 on tree, 136107.91 best solution, best possible 132202.59 (1501.14 seconds)\n", + "Cbc0010I After 128300 nodes, 18410 on tree, 136107.91 best solution, best possible 132202.59 (1502.23 seconds)\n", + "Cbc0010I After 128400 nodes, 18403 on tree, 136107.91 best solution, best possible 132202.59 (1503.16 seconds)\n", + "Cbc0010I After 128500 nodes, 18403 on tree, 136107.91 best solution, best possible 132202.59 (1504.01 seconds)\n", + "Cbc0010I After 128600 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1505.37 seconds)\n", + "Cbc0010I After 128700 nodes, 18408 on tree, 136107.91 best solution, best possible 132202.59 (1506.65 seconds)\n", + "Cbc0010I After 128800 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1508.33 seconds)\n", + "Cbc0010I After 128900 nodes, 18400 on tree, 136107.91 best solution, best possible 132202.59 (1509.98 seconds)\n", + "Cbc0010I After 129000 nodes, 18398 on tree, 136107.91 best solution, best possible 132202.59 (1511.84 seconds)\n", + "Cbc0010I After 129100 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1513.52 seconds)\n", + "Cbc0010I After 129200 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1515.26 seconds)\n", + "Cbc0010I After 129300 nodes, 18414 on tree, 136107.91 best solution, best possible 132202.59 (1516.78 seconds)\n", + "Cbc0010I After 129400 nodes, 18406 on tree, 136107.91 best solution, best possible 132202.59 (1517.89 seconds)\n", + "Cbc0010I After 129500 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1519.08 seconds)\n", + "Cbc0010I After 129600 nodes, 18413 on tree, 136107.91 best solution, best possible 132202.59 (1520.63 seconds)\n", + "Cbc0010I After 129700 nodes, 18406 on tree, 136107.91 best solution, best possible 132202.59 (1522.07 seconds)\n", + "Cbc0010I After 129800 nodes, 18417 on tree, 136107.91 best solution, best possible 132202.59 (1522.96 seconds)\n", + "Cbc0010I After 129900 nodes, 18405 on tree, 136107.91 best solution, best possible 132202.59 (1523.86 seconds)\n", + "Cbc0010I After 130000 nodes, 18407 on tree, 136107.91 best solution, best possible 132202.59 (1524.96 seconds)\n", + "Cbc0010I After 130100 nodes, 18404 on tree, 136107.91 best solution, best possible 132202.59 (1525.86 seconds)\n", + "Cbc0010I After 130200 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1526.96 seconds)\n", + "Cbc0010I After 130300 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1528.31 seconds)\n", + "Cbc0010I After 130400 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1529.88 seconds)\n", + "Cbc0010I After 130500 nodes, 18401 on tree, 136107.91 best solution, best possible 132202.59 (1531.16 seconds)\n", + "Cbc0010I After 130600 nodes, 18399 on tree, 136107.91 best solution, best possible 132202.59 (1532.39 seconds)\n", + "Cbc0010I After 130700 nodes, 18362 on tree, 136107.91 best solution, best possible 132202.59 (1533.99 seconds)\n", + "Cbc0010I After 130800 nodes, 18335 on tree, 136107.91 best solution, best possible 132202.59 (1535.64 seconds)\n", + "Cbc0010I After 130900 nodes, 18299 on tree, 136107.91 best solution, best possible 132202.59 (1536.75 seconds)\n", + "Cbc0010I After 131000 nodes, 18273 on tree, 136107.91 best solution, best possible 132202.59 (1537.95 seconds)\n", + "Cbc0010I After 131100 nodes, 18254 on tree, 136107.91 best solution, best possible 132202.59 (1539.25 seconds)\n", + "Cbc0010I After 131200 nodes, 18241 on tree, 136107.91 best solution, best possible 132202.59 (1540.35 seconds)\n", + "Cbc0010I After 131300 nodes, 18198 on tree, 136107.91 best solution, best possible 132202.59 (1541.77 seconds)\n", + "Cbc0010I After 131400 nodes, 18169 on tree, 136107.91 best solution, best possible 132202.59 (1543.02 seconds)\n", + "Cbc0010I After 131500 nodes, 18164 on tree, 136107.91 best solution, best possible 132202.59 (1544.36 seconds)\n", + "Cbc0010I After 131600 nodes, 18144 on tree, 136107.91 best solution, best possible 132202.59 (1546.25 seconds)\n", + "Cbc0010I After 131700 nodes, 18136 on tree, 136107.91 best solution, best possible 132202.59 (1548.02 seconds)\n", + "Cbc0010I After 131800 nodes, 18115 on tree, 136107.91 best solution, best possible 132202.59 (1549.26 seconds)\n", + "Cbc0010I After 131900 nodes, 18103 on tree, 136107.91 best solution, best possible 132202.59 (1550.27 seconds)\n", + "Cbc0010I After 132000 nodes, 18086 on tree, 136107.91 best solution, best possible 132202.59 (1551.70 seconds)\n", + "Cbc0010I After 132100 nodes, 18080 on tree, 136107.91 best solution, best possible 132202.59 (1552.50 seconds)\n", + "Cbc0010I After 132200 nodes, 18070 on tree, 136107.91 best solution, best possible 132202.59 (1553.38 seconds)\n", + "Cbc0010I After 132300 nodes, 18075 on tree, 136107.91 best solution, best possible 132202.59 (1554.37 seconds)\n", + "Cbc0010I After 132400 nodes, 18078 on tree, 136107.91 best solution, best possible 132202.59 (1555.43 seconds)\n", + "Cbc0010I After 132500 nodes, 18077 on tree, 136107.91 best solution, best possible 132202.59 (1556.37 seconds)\n", + "Cbc0010I After 132600 nodes, 18069 on tree, 136107.91 best solution, best possible 132202.59 (1557.33 seconds)\n", + "Cbc0010I After 132700 nodes, 18065 on tree, 136107.91 best solution, best possible 132202.59 (1558.76 seconds)\n", + "Cbc0010I After 132800 nodes, 18062 on tree, 136107.91 best solution, best possible 132202.59 (1560.37 seconds)\n", + "Cbc0010I After 132900 nodes, 18065 on tree, 136107.91 best solution, best possible 132202.59 (1561.71 seconds)\n", + "Cbc0010I After 133000 nodes, 18059 on tree, 136107.91 best solution, best possible 132202.59 (1562.75 seconds)\n", + "Cbc0010I After 133100 nodes, 18107 on tree, 136107.91 best solution, best possible 132206.15 (1566.78 seconds)\n", + "Cbc0010I After 133200 nodes, 18153 on tree, 136107.91 best solution, best possible 132209.18 (1569.68 seconds)\n", + "Cbc0010I After 133300 nodes, 18197 on tree, 136107.91 best solution, best possible 132212.83 (1573.06 seconds)\n", + "Cbc0010I After 133400 nodes, 18242 on tree, 136107.91 best solution, best possible 132216 (1575.65 seconds)\n", + "Cbc0010I After 133500 nodes, 18283 on tree, 136107.91 best solution, best possible 132220.37 (1577.92 seconds)\n", + "Cbc0010I After 133600 nodes, 18330 on tree, 136107.91 best solution, best possible 132222.86 (1580.25 seconds)\n", + "Cbc0010I After 133700 nodes, 18378 on tree, 136107.91 best solution, best possible 132225.79 (1582.59 seconds)\n", + "Cbc0010I After 133800 nodes, 18422 on tree, 136107.91 best solution, best possible 132229.49 (1585.66 seconds)\n", + "Cbc0010I After 133900 nodes, 18464 on tree, 136107.91 best solution, best possible 132233.07 (1588.19 seconds)\n", + "Cbc0010I After 134000 nodes, 18509 on tree, 136107.91 best solution, best possible 132236.07 (1590.39 seconds)\n", + "Cbc0010I After 134100 nodes, 18505 on tree, 136107.91 best solution, best possible 132236.07 (1590.84 seconds)\n", + "Cbc0010I After 134200 nodes, 18524 on tree, 136107.91 best solution, best possible 132236.07 (1592.21 seconds)\n", + "Cbc0010I After 134300 nodes, 18516 on tree, 136107.91 best solution, best possible 132236.07 (1593.16 seconds)\n", + "Cbc0010I After 134400 nodes, 18515 on tree, 136107.91 best solution, best possible 132236.07 (1594.64 seconds)\n", + "Cbc0010I After 134500 nodes, 18514 on tree, 136107.91 best solution, best possible 132236.07 (1595.69 seconds)\n", + "Cbc0010I After 134600 nodes, 18510 on tree, 136107.91 best solution, best possible 132236.07 (1596.84 seconds)\n", + "Cbc0010I After 134700 nodes, 18504 on tree, 136107.91 best solution, best possible 132236.07 (1598.48 seconds)\n", + "Cbc0010I After 134800 nodes, 18506 on tree, 136107.91 best solution, best possible 132236.07 (1599.70 seconds)\n", + "Cbc0010I After 134900 nodes, 18487 on tree, 136107.91 best solution, best possible 132236.07 (1600.73 seconds)\n", + "Cbc0010I After 135000 nodes, 18470 on tree, 136107.91 best solution, best possible 132236.07 (1601.85 seconds)\n", + "Cbc0010I After 135100 nodes, 18477 on tree, 136107.91 best solution, best possible 132236.07 (1602.62 seconds)\n", + "Cbc0010I After 135200 nodes, 18470 on tree, 136107.91 best solution, best possible 132236.07 (1603.31 seconds)\n", + "Cbc0010I After 135300 nodes, 18467 on tree, 136107.91 best solution, best possible 132236.07 (1603.94 seconds)\n", + "Cbc0010I After 135400 nodes, 18448 on tree, 136107.91 best solution, best possible 132236.07 (1605.16 seconds)\n", + "Cbc0010I After 135500 nodes, 18439 on tree, 136107.91 best solution, best possible 132236.07 (1606.35 seconds)\n", + "Cbc0010I After 135600 nodes, 18426 on tree, 136107.91 best solution, best possible 132236.07 (1607.21 seconds)\n", + "Cbc0010I After 135700 nodes, 18424 on tree, 136107.91 best solution, best possible 132236.07 (1608.07 seconds)\n", + "Cbc0010I After 135800 nodes, 18417 on tree, 136107.91 best solution, best possible 132236.07 (1609.18 seconds)\n", + "Cbc0010I After 135900 nodes, 18409 on tree, 136107.91 best solution, best possible 132236.07 (1610.83 seconds)\n", + "Cbc0010I After 136000 nodes, 18406 on tree, 136107.91 best solution, best possible 132236.07 (1612.61 seconds)\n", + "Cbc0010I After 136100 nodes, 18390 on tree, 136107.91 best solution, best possible 132236.07 (1613.62 seconds)\n", + "Cbc0010I After 136200 nodes, 18392 on tree, 136107.91 best solution, best possible 132236.07 (1614.83 seconds)\n", + "Cbc0010I After 136300 nodes, 18387 on tree, 136107.91 best solution, best possible 132236.07 (1615.77 seconds)\n", + "Cbc0010I After 136400 nodes, 18390 on tree, 136107.91 best solution, best possible 132236.07 (1616.69 seconds)\n", + "Cbc0010I After 136500 nodes, 18379 on tree, 136107.91 best solution, best possible 132236.07 (1617.81 seconds)\n", + "Cbc0010I After 136600 nodes, 18364 on tree, 136107.91 best solution, best possible 132236.07 (1618.89 seconds)\n", + "Cbc0010I After 136700 nodes, 18367 on tree, 136107.91 best solution, best possible 132236.07 (1619.94 seconds)\n", + "Cbc0010I After 136800 nodes, 18357 on tree, 136107.91 best solution, best possible 132236.07 (1621.22 seconds)\n", + "Cbc0010I After 136900 nodes, 18360 on tree, 136107.91 best solution, best possible 132236.07 (1622.34 seconds)\n", + "Cbc0010I After 137000 nodes, 18358 on tree, 136107.91 best solution, best possible 132236.07 (1623.66 seconds)\n", + "Cbc0010I After 137100 nodes, 18354 on tree, 136107.91 best solution, best possible 132236.07 (1625.07 seconds)\n", + "Cbc0010I After 137200 nodes, 18339 on tree, 136107.91 best solution, best possible 132236.07 (1626.18 seconds)\n", + "Cbc0010I After 137300 nodes, 18341 on tree, 136107.91 best solution, best possible 132236.07 (1627.32 seconds)\n", + "Cbc0010I After 137400 nodes, 18325 on tree, 136107.91 best solution, best possible 132236.07 (1628.33 seconds)\n", + "Cbc0010I After 137500 nodes, 18327 on tree, 136107.91 best solution, best possible 132236.07 (1628.93 seconds)\n", + "Cbc0010I After 137600 nodes, 18327 on tree, 136107.91 best solution, best possible 132236.07 (1629.82 seconds)\n", + "Cbc0010I After 137700 nodes, 18329 on tree, 136107.91 best solution, best possible 132236.07 (1631.32 seconds)\n", + "Cbc0010I After 137800 nodes, 18327 on tree, 136107.91 best solution, best possible 132236.07 (1632.68 seconds)\n", + "Cbc0010I After 137900 nodes, 18333 on tree, 136107.91 best solution, best possible 132236.07 (1634.42 seconds)\n", + "Cbc0010I After 138000 nodes, 18332 on tree, 136107.91 best solution, best possible 132236.07 (1637.03 seconds)\n", + "Cbc0010I After 138100 nodes, 18331 on tree, 136107.91 best solution, best possible 132236.07 (1638.77 seconds)\n", + "Cbc0010I After 138200 nodes, 18319 on tree, 136107.91 best solution, best possible 132236.07 (1640.12 seconds)\n", + "Cbc0010I After 138300 nodes, 18303 on tree, 136107.91 best solution, best possible 132236.07 (1641.30 seconds)\n", + "Cbc0010I After 138400 nodes, 18302 on tree, 136107.91 best solution, best possible 132236.07 (1642.64 seconds)\n", + "Cbc0010I After 138500 nodes, 18300 on tree, 136107.91 best solution, best possible 132236.07 (1644.14 seconds)\n", + "Cbc0010I After 138600 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1645.42 seconds)\n", + "Cbc0010I After 138700 nodes, 18300 on tree, 136107.91 best solution, best possible 132236.07 (1646.75 seconds)\n", + "Cbc0010I After 138800 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1648.13 seconds)\n", + "Cbc0010I After 138900 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1649.91 seconds)\n", + "Cbc0010I After 139000 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1651.16 seconds)\n", + "Cbc0010I After 139100 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1651.95 seconds)\n", + "Cbc0010I After 139200 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1652.84 seconds)\n", + "Cbc0010I After 139300 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1653.68 seconds)\n", + "Cbc0010I After 139400 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1654.46 seconds)\n", + "Cbc0010I After 139500 nodes, 18299 on tree, 136107.91 best solution, best possible 132236.07 (1655.46 seconds)\n", + "Cbc0010I After 139600 nodes, 18286 on tree, 136107.91 best solution, best possible 132236.07 (1656.10 seconds)\n", + "Cbc0010I After 139700 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1657.28 seconds)\n", + "Cbc0010I After 139800 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1658.23 seconds)\n", + "Cbc0010I After 139900 nodes, 18303 on tree, 136107.91 best solution, best possible 132236.07 (1659.40 seconds)\n", + "Cbc0010I After 140000 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1660.09 seconds)\n", + "Cbc0010I After 140100 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1661.50 seconds)\n", + "Cbc0010I After 140200 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1663.44 seconds)\n", + "Cbc0010I After 140300 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1664.60 seconds)\n", + "Cbc0010I After 140400 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1665.89 seconds)\n", + "Cbc0010I After 140500 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1667.05 seconds)\n", + "Cbc0010I After 140600 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1668.16 seconds)\n", + "Cbc0010I After 140700 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1669.17 seconds)\n", + "Cbc0010I After 140800 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1670.29 seconds)\n", + "Cbc0010I After 140900 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1671.14 seconds)\n", + "Cbc0010I After 141000 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1672.18 seconds)\n", + "Cbc0010I After 141100 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1673.24 seconds)\n", + "Cbc0010I After 141200 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1675.71 seconds)\n", + "Cbc0010I After 141300 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1676.98 seconds)\n", + "Cbc0010I After 141400 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1678.17 seconds)\n", + "Cbc0010I After 141500 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1679.02 seconds)\n", + "Cbc0010I After 141600 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1680.14 seconds)\n", + "Cbc0010I After 141700 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1680.89 seconds)\n", + "Cbc0010I After 141800 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1681.65 seconds)\n", + "Cbc0010I After 141900 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1682.96 seconds)\n", + "Cbc0010I After 142000 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1684.02 seconds)\n", + "Cbc0010I After 142100 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1685.30 seconds)\n", + "Cbc0010I After 142200 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1686.76 seconds)\n", + "Cbc0010I After 142300 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1688.84 seconds)\n", + "Cbc0010I After 142400 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1690.16 seconds)\n", + "Cbc0010I After 142500 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1691.21 seconds)\n", + "Cbc0010I After 142600 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1692.39 seconds)\n", + "Cbc0010I After 142700 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1693.83 seconds)\n", + "Cbc0010I After 142800 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1695.45 seconds)\n", + "Cbc0010I After 142900 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1696.20 seconds)\n", + "Cbc0010I After 143000 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1697.03 seconds)\n", + "Cbc0010I After 143100 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1698.15 seconds)\n", + "Cbc0010I After 143200 nodes, 18299 on tree, 136107.91 best solution, best possible 132236.07 (1699.56 seconds)\n", + "Cbc0010I After 143300 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1701.55 seconds)\n", + "Cbc0010I After 143400 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1702.93 seconds)\n", + "Cbc0010I After 143500 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1703.88 seconds)\n", + "Cbc0010I After 143600 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1704.88 seconds)\n", + "Cbc0010I After 143700 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1706.41 seconds)\n", + "Cbc0010I After 143800 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1707.29 seconds)\n", + "Cbc0010I After 143900 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1708.62 seconds)\n", + "Cbc0010I After 144000 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1709.41 seconds)\n", + "Cbc0010I After 144100 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1710.61 seconds)\n", + "Cbc0010I After 144200 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1711.54 seconds)\n", + "Cbc0010I After 144300 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1713.46 seconds)\n", + "Cbc0010I After 144400 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1714.96 seconds)\n", + "Cbc0010I After 144500 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1716.43 seconds)\n", + "Cbc0010I After 144600 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1717.92 seconds)\n", + "Cbc0010I After 144700 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1718.92 seconds)\n", + "Cbc0010I After 144800 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1719.95 seconds)\n", + "Cbc0010I After 144900 nodes, 18298 on tree, 136107.91 best solution, best possible 132236.07 (1721.25 seconds)\n", + "Cbc0010I After 145000 nodes, 18308 on tree, 136107.91 best solution, best possible 132236.07 (1722.19 seconds)\n", + "Cbc0010I After 145100 nodes, 18302 on tree, 136107.91 best solution, best possible 132236.07 (1723.16 seconds)\n", + "Cbc0010I After 145200 nodes, 18302 on tree, 136107.91 best solution, best possible 132236.07 (1724.29 seconds)\n", + "Cbc0010I After 145300 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1725.74 seconds)\n", + "Cbc0010I After 145400 nodes, 18299 on tree, 136107.91 best solution, best possible 132236.07 (1727.00 seconds)\n", + "Cbc0010I After 145500 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1727.71 seconds)\n", + "Cbc0010I After 145600 nodes, 18298 on tree, 136107.91 best solution, best possible 132236.07 (1728.75 seconds)\n", + "Cbc0010I After 145700 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1729.65 seconds)\n", + "Cbc0010I After 145800 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1731.10 seconds)\n", + "Cbc0010I After 145900 nodes, 18299 on tree, 136107.91 best solution, best possible 132236.07 (1732.51 seconds)\n", + "Cbc0010I After 146000 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1733.65 seconds)\n", + "Cbc0010I After 146100 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1734.81 seconds)\n", + "Cbc0010I After 146200 nodes, 18299 on tree, 136107.91 best solution, best possible 132236.07 (1735.96 seconds)\n", + "Cbc0010I After 146300 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1736.80 seconds)\n", + "Cbc0010I After 146400 nodes, 18305 on tree, 136107.91 best solution, best possible 132236.07 (1739.17 seconds)\n", + "Cbc0010I After 146500 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1740.34 seconds)\n", + "Cbc0010I After 146600 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1741.46 seconds)\n", + "Cbc0010I After 146700 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1742.35 seconds)\n", + "Cbc0010I After 146800 nodes, 18298 on tree, 136107.91 best solution, best possible 132236.07 (1743.37 seconds)\n", + "Cbc0010I After 146900 nodes, 18305 on tree, 136107.91 best solution, best possible 132236.07 (1744.20 seconds)\n", + "Cbc0010I After 147000 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1745.22 seconds)\n", + "Cbc0010I After 147100 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1746.31 seconds)\n", + "Cbc0010I After 147200 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1747.50 seconds)\n", + "Cbc0010I After 147300 nodes, 18304 on tree, 136107.91 best solution, best possible 132236.07 (1749.23 seconds)\n", + "Cbc0010I After 147400 nodes, 18299 on tree, 136107.91 best solution, best possible 132236.07 (1750.22 seconds)\n", + "Cbc0010I After 147500 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1752.47 seconds)\n", + "Cbc0010I After 147600 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1754.10 seconds)\n", + "Cbc0010I After 147700 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1755.60 seconds)\n", + "Cbc0010I After 147800 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1756.96 seconds)\n", + "Cbc0010I After 147900 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1758.27 seconds)\n", + "Cbc0010I After 148000 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1759.71 seconds)\n", + "Cbc0010I After 148100 nodes, 18301 on tree, 136107.91 best solution, best possible 132236.07 (1761.14 seconds)\n", + "Cbc0010I After 148200 nodes, 18298 on tree, 136107.91 best solution, best possible 132236.07 (1762.69 seconds)\n", + "Cbc0010I After 148300 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1764.84 seconds)\n", + "Cbc0010I After 148400 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1766.63 seconds)\n", + "Cbc0010I After 148500 nodes, 18300 on tree, 136107.91 best solution, best possible 132236.07 (1767.90 seconds)\n", + "Cbc0010I After 148600 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1769.23 seconds)\n", + "Cbc0010I After 148700 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1770.61 seconds)\n", + "Cbc0010I After 148800 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1771.49 seconds)\n", + "Cbc0010I After 148900 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1772.94 seconds)\n", + "Cbc0010I After 149000 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1774.64 seconds)\n", + "Cbc0010I After 149100 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1776.08 seconds)\n", + "Cbc0010I After 149200 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1778.27 seconds)\n", + "Cbc0010I After 149300 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1779.70 seconds)\n", + "Cbc0010I After 149400 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1780.88 seconds)\n", + "Cbc0010I After 149500 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1782.32 seconds)\n", + "Cbc0010I After 149600 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1783.84 seconds)\n", + "Cbc0010I After 149700 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1785.34 seconds)\n", + "Cbc0010I After 149800 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1786.72 seconds)\n", + "Cbc0010I After 149900 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1787.89 seconds)\n", + "Cbc0010I After 150000 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1789.24 seconds)\n", + "Cbc0010I After 150100 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1791.97 seconds)\n", + "Cbc0010I After 150200 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1793.41 seconds)\n", + "Cbc0010I After 150300 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1794.47 seconds)\n", + "Cbc0010I After 150400 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1795.63 seconds)\n", + "Cbc0010I After 150500 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1796.78 seconds)\n", + "Cbc0010I After 150600 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1797.79 seconds)\n", + "Cbc0010I After 150700 nodes, 18302 on tree, 136107.91 best solution, best possible 132236.07 (1799.06 seconds)\n", + "Cbc0010I After 150800 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1800.33 seconds)\n", + "Cbc0010I After 150900 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1801.31 seconds)\n", + "Cbc0010I After 151000 nodes, 18303 on tree, 136107.91 best solution, best possible 132236.07 (1803.51 seconds)\n", + "Cbc0010I After 151100 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1806.02 seconds)\n", + "Cbc0010I After 151200 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1807.52 seconds)\n", + "Cbc0010I After 151300 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1808.67 seconds)\n", + "Cbc0010I After 151400 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1809.59 seconds)\n", + "Cbc0010I After 151500 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1811.04 seconds)\n", + "Cbc0010I After 151600 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1812.00 seconds)\n", + "Cbc0010I After 151700 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1813.34 seconds)\n", + "Cbc0010I After 151800 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1814.41 seconds)\n", + "Cbc0010I After 151900 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1815.73 seconds)\n", + "Cbc0010I After 152000 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1817.55 seconds)\n", + "Cbc0010I After 152100 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1819.41 seconds)\n", + "Cbc0010I After 152200 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1820.65 seconds)\n", + "Cbc0010I After 152300 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1822.30 seconds)\n", + "Cbc0010I After 152400 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1823.73 seconds)\n", + "Cbc0010I After 152500 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1825.14 seconds)\n", + "Cbc0010I After 152600 nodes, 18298 on tree, 136107.91 best solution, best possible 132236.07 (1826.94 seconds)\n", + "Cbc0010I After 152700 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1828.20 seconds)\n", + "Cbc0010I After 152800 nodes, 18301 on tree, 136107.91 best solution, best possible 132236.07 (1829.66 seconds)\n", + "Cbc0010I After 152900 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1831.74 seconds)\n", + "Cbc0010I After 153000 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1833.25 seconds)\n", + "Cbc0010I After 153100 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1834.35 seconds)\n", + "Cbc0010I After 153200 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1835.61 seconds)\n", + "Cbc0010I After 153300 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1837.86 seconds)\n", + "Cbc0010I After 153400 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1839.56 seconds)\n", + "Cbc0010I After 153500 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1840.79 seconds)\n", + "Cbc0010I After 153600 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1841.85 seconds)\n", + "Cbc0010I After 153700 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1843.89 seconds)\n", + "Cbc0010I After 153800 nodes, 18298 on tree, 136107.91 best solution, best possible 132236.07 (1845.66 seconds)\n", + "Cbc0010I After 153900 nodes, 18298 on tree, 136107.91 best solution, best possible 132236.07 (1846.87 seconds)\n", + "Cbc0010I After 154000 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1848.16 seconds)\n", + "Cbc0010I After 154100 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1849.46 seconds)\n", + "Cbc0010I After 154200 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1851.88 seconds)\n", + "Cbc0010I After 154300 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1853.67 seconds)\n", + "Cbc0010I After 154400 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1855.29 seconds)\n", + "Cbc0010I After 154500 nodes, 18306 on tree, 136107.91 best solution, best possible 132236.07 (1856.95 seconds)\n", + "Cbc0010I After 154600 nodes, 18302 on tree, 136107.91 best solution, best possible 132236.07 (1857.85 seconds)\n", + "Cbc0010I After 154700 nodes, 18298 on tree, 136107.91 best solution, best possible 132236.07 (1858.42 seconds)\n", + "Cbc0010I After 154800 nodes, 18300 on tree, 136107.91 best solution, best possible 132236.07 (1859.00 seconds)\n", + "Cbc0010I After 154900 nodes, 18300 on tree, 136107.91 best solution, best possible 132236.07 (1859.62 seconds)\n", + "Cbc0010I After 155000 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1860.40 seconds)\n", + "Cbc0010I After 155100 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1861.05 seconds)\n", + "Cbc0010I After 155200 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1861.64 seconds)\n", + "Cbc0010I After 155300 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1862.57 seconds)\n", + "Cbc0010I After 155400 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1863.49 seconds)\n", + "Cbc0010I After 155500 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1864.30 seconds)\n", + "Cbc0010I After 155600 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1865.25 seconds)\n", + "Cbc0010I After 155700 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1866.44 seconds)\n", + "Cbc0010I After 155800 nodes, 18300 on tree, 136107.91 best solution, best possible 132236.07 (1867.98 seconds)\n", + "Cbc0010I After 155900 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1869.75 seconds)\n", + "Cbc0010I After 156000 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1871.37 seconds)\n", + "Cbc0010I After 156100 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1872.98 seconds)\n", + "Cbc0010I After 156200 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1874.14 seconds)\n", + "Cbc0010I After 156300 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1875.25 seconds)\n", + "Cbc0010I After 156400 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1876.24 seconds)\n", + "Cbc0010I After 156500 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1878.45 seconds)\n", + "Cbc0010I After 156600 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1880.10 seconds)\n", + "Cbc0010I After 156700 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1882.01 seconds)\n", + "Cbc0010I After 156800 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1883.90 seconds)\n", + "Cbc0010I After 156900 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1885.17 seconds)\n", + "Cbc0010I After 157000 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1886.77 seconds)\n", + "Cbc0010I After 157100 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1888.12 seconds)\n", + "Cbc0010I After 157200 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1888.88 seconds)\n", + "Cbc0010I After 157300 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1890.14 seconds)\n", + "Cbc0010I After 157400 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1891.95 seconds)\n", + "Cbc0010I After 157500 nodes, 18310 on tree, 136107.91 best solution, best possible 132236.07 (1893.19 seconds)\n", + "Cbc0010I After 157600 nodes, 18301 on tree, 136107.91 best solution, best possible 132236.07 (1895.40 seconds)\n", + "Cbc0010I After 157700 nodes, 18297 on tree, 136107.91 best solution, best possible 132236.07 (1896.67 seconds)\n", + "Cbc0010I After 157800 nodes, 18301 on tree, 136107.91 best solution, best possible 132236.07 (1897.79 seconds)\n", + "Cbc0010I After 157900 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1898.91 seconds)\n", + "Cbc0010I After 158000 nodes, 18296 on tree, 136107.91 best solution, best possible 132236.07 (1900.28 seconds)\n", + "Cbc0010I After 158100 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1901.42 seconds)\n", + "Cbc0010I After 158200 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1902.72 seconds)\n", + "Cbc0010I After 158300 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1904.55 seconds)\n", + "Cbc0010I After 158400 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1905.97 seconds)\n", + "Cbc0010I After 158500 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1908.52 seconds)\n", + "Cbc0010I After 158600 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1910.83 seconds)\n", + "Cbc0010I After 158700 nodes, 18298 on tree, 136107.91 best solution, best possible 132236.07 (1912.65 seconds)\n", + "Cbc0010I After 158800 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1914.17 seconds)\n", + "Cbc0010I After 158900 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1915.41 seconds)\n", + "Cbc0010I After 159000 nodes, 18299 on tree, 136107.91 best solution, best possible 132236.07 (1916.37 seconds)\n", + "Cbc0010I After 159100 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1917.42 seconds)\n", + "Cbc0010I After 159200 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1918.28 seconds)\n", + "Cbc0010I After 159300 nodes, 18294 on tree, 136107.91 best solution, best possible 132236.07 (1919.46 seconds)\n", + "Cbc0010I After 159400 nodes, 18302 on tree, 136107.91 best solution, best possible 132236.07 (1921.19 seconds)\n", + "Cbc0010I After 159500 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1922.16 seconds)\n", + "Cbc0010I After 159600 nodes, 18298 on tree, 136107.91 best solution, best possible 132236.07 (1922.95 seconds)\n", + "Cbc0010I After 159700 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1923.79 seconds)\n", + "Cbc0010I After 159800 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1925.29 seconds)\n", + "Cbc0010I After 159900 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1926.95 seconds)\n", + "Cbc0010I After 160000 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1928.35 seconds)\n", + "Cbc0010I After 160100 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1929.81 seconds)\n", + "Cbc0010I After 160200 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1931.38 seconds)\n", + "Cbc0010I After 160300 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1933.51 seconds)\n", + "Cbc0010I After 160400 nodes, 18299 on tree, 136107.91 best solution, best possible 132236.07 (1934.89 seconds)\n", + "Cbc0010I After 160500 nodes, 18295 on tree, 136107.91 best solution, best possible 132236.07 (1935.96 seconds)\n", + "Cbc0010I After 160600 nodes, 18292 on tree, 136107.91 best solution, best possible 132236.07 (1936.81 seconds)\n", + "Cbc0010I After 160700 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1938.14 seconds)\n", + "Cbc0010I After 160800 nodes, 18289 on tree, 136107.91 best solution, best possible 132236.07 (1939.58 seconds)\n", + "Cbc0010I After 160900 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1940.94 seconds)\n", + "Cbc0010I After 161000 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1942.91 seconds)\n", + "Cbc0010I After 161100 nodes, 18290 on tree, 136107.91 best solution, best possible 132236.07 (1944.19 seconds)\n", + "Cbc0010I After 161200 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1947.52 seconds)\n", + "Cbc0010I After 161300 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1949.02 seconds)\n", + "Cbc0010I After 161400 nodes, 18293 on tree, 136107.91 best solution, best possible 132236.07 (1950.10 seconds)\n", + "Cbc0010I After 161500 nodes, 18287 on tree, 136107.91 best solution, best possible 132236.07 (1951.24 seconds)\n", + "Cbc0010I After 161600 nodes, 18291 on tree, 136107.91 best solution, best possible 132236.07 (1952.53 seconds)\n", + "Cbc0010I After 161700 nodes, 18248 on tree, 136107.91 best solution, best possible 132236.07 (1953.48 seconds)\n", + "Cbc0010I After 161800 nodes, 18219 on tree, 136107.91 best solution, best possible 132236.07 (1954.34 seconds)\n", + "Cbc0010I After 161900 nodes, 18213 on tree, 136107.91 best solution, best possible 132236.07 (1955.27 seconds)\n", + "Cbc0010I After 162000 nodes, 18196 on tree, 136107.91 best solution, best possible 132236.07 (1956.24 seconds)\n", + "Cbc0010I After 162100 nodes, 18242 on tree, 136107.91 best solution, best possible 132236.07 (1960.01 seconds)\n", + "Cbc0010I After 162200 nodes, 18288 on tree, 136107.91 best solution, best possible 132236.07 (1962.47 seconds)\n", + "Cbc0010I After 162300 nodes, 18334 on tree, 136107.91 best solution, best possible 132236.07 (1964.60 seconds)\n", + "Cbc0010I After 162400 nodes, 18379 on tree, 136107.91 best solution, best possible 132236.07 (1966.86 seconds)\n", + "Cbc0010I After 162500 nodes, 18419 on tree, 136107.91 best solution, best possible 132236.07 (1968.56 seconds)\n", + "Cbc0010I After 162600 nodes, 18460 on tree, 136107.91 best solution, best possible 132236.07 (1971.10 seconds)\n", + "Cbc0010I After 162700 nodes, 18502 on tree, 136107.91 best solution, best possible 132236.07 (1973.65 seconds)\n", + "Cbc0010I After 162800 nodes, 18547 on tree, 136107.91 best solution, best possible 132236.07 (1975.50 seconds)\n", + "Cbc0010I After 162900 nodes, 18591 on tree, 136107.91 best solution, best possible 132236.07 (1977.18 seconds)\n", + "Cbc0010I After 163000 nodes, 18638 on tree, 136107.91 best solution, best possible 132236.07 (1978.96 seconds)\n", + "Cbc0010I After 163100 nodes, 18643 on tree, 136107.91 best solution, best possible 132236.07 (1980.04 seconds)\n", + "Cbc0010I After 163200 nodes, 18639 on tree, 136107.91 best solution, best possible 132236.07 (1980.87 seconds)\n", + "Cbc0010I After 163300 nodes, 18642 on tree, 136107.91 best solution, best possible 132236.07 (1981.86 seconds)\n", + "Cbc0010I After 163400 nodes, 18640 on tree, 136107.91 best solution, best possible 132236.07 (1982.73 seconds)\n", + "Cbc0010I After 163500 nodes, 18630 on tree, 136107.91 best solution, best possible 132236.07 (1984.57 seconds)\n", + "Cbc0010I After 163600 nodes, 18632 on tree, 136107.91 best solution, best possible 132236.07 (1986.06 seconds)\n", + "Cbc0010I After 163700 nodes, 18627 on tree, 136107.91 best solution, best possible 132236.07 (1987.18 seconds)\n", + "Cbc0010I After 163800 nodes, 18619 on tree, 136107.91 best solution, best possible 132236.07 (1988.24 seconds)\n", + "Cbc0010I After 163900 nodes, 18588 on tree, 136107.91 best solution, best possible 132236.07 (1989.21 seconds)\n", + "Cbc0010I After 164000 nodes, 18582 on tree, 136107.91 best solution, best possible 132236.07 (1990.16 seconds)\n", + "Cbc0010I After 164100 nodes, 18562 on tree, 136107.91 best solution, best possible 132236.07 (1991.22 seconds)\n", + "Cbc0010I After 164200 nodes, 18550 on tree, 136107.91 best solution, best possible 132236.07 (1992.18 seconds)\n", + "Cbc0010I After 164300 nodes, 18533 on tree, 136107.91 best solution, best possible 132236.07 (1993.22 seconds)\n", + "Cbc0010I After 164400 nodes, 18516 on tree, 136107.91 best solution, best possible 132236.07 (1994.09 seconds)\n", + "Cbc0010I After 164500 nodes, 18501 on tree, 136107.91 best solution, best possible 132236.07 (1995.05 seconds)\n", + "Cbc0010I After 164600 nodes, 18494 on tree, 136107.91 best solution, best possible 132236.07 (1996.17 seconds)\n", + "Cbc0010I After 164700 nodes, 18471 on tree, 136107.91 best solution, best possible 132236.07 (1998.42 seconds)\n", + "Cbc0030I Thread 0 used 82247 times, waiting to start 22.336627, 459858 locks, 32.331118 locked, 1.8035834 waiting for locks\n", + "Cbc0030I Thread 1 used 82550 times, waiting to start 23.465048, 461399 locks, 32.145875 locked, 2.0258605 waiting for locks\n", + "Cbc0030I Main thread 1970.6652 waiting for threads, 331418 locks, 3.0709093 locked, 1.1453919 waiting for locks\n", + "Cbc0020I Exiting on maximum time\n", + "Cbc0005I Partial search - best objective 136107.91 (best possible 132236.07), took 7895568 iterations and 164796 nodes (2002.03 seconds)\n", + "Cbc0032I Strong branching done 160026 times (4647759 iterations), fathomed 7770 nodes and fixed 35515 variables\n", + "Cbc0035I Maximum depth 66, 2575441 variables fixed on reduced cost\n", + "Cuts at root node changed objective from 127486 to 130203\n", + "Probing was tried 60 times and created 3 cuts of which 0 were active after adding rounds of cuts (0.182 seconds)\n", + "Gomory was tried 50228 times and created 34877 cuts of which 0 were active after adding rounds of cuts (243.466 seconds)\n", + "Knapsack was tried 60 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.041 seconds)\n", + "Clique was tried 60 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.008 seconds)\n", + "MixedIntegerRounding2 was tried 60 times and created 9 cuts of which 0 were active after adding rounds of cuts (0.143 seconds)\n", + "FlowCover was tried 50244 times and created 255328 cuts of which 0 were active after adding rounds of cuts (67.561 seconds)\n", + "TwoMirCuts was tried 50228 times and created 22651 cuts of which 0 were active after adding rounds of cuts (63.754 seconds)\n", + "ZeroHalf was tried 60 times and created 45 cuts of which 0 were active after adding rounds of cuts (0.295 seconds)\n", + "\n", + "Result - Stopped on time limit\n", + "\n", + "Objective value: 136107.91373610\n", + "Lower bound: 132236.066\n", + "Gap: 0.03\n", + "Enumerated nodes: 164796\n", + "Total iterations: 7895568\n", + "Time (CPU seconds): 3460.30\n", + "Time (Wallclock seconds): 2002.07\n", + "\n", + "Total time (CPU seconds): 3460.32 (Wallclock seconds): 2002.10\n", + "\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "WARNING:pyomo.core:Loading a SolverResults object with an 'aborted' status, but containing a solution\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "wfn.plot()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 445 + }, + "id": "hZXVqaiu4OvN", + "outputId": "d20771ea-48a7-443b-9319-637939bbc5a8" + }, + "execution_count": 21, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ], + "image/png": "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\n" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "print(round(cost), ' €')" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bIn4ebtC_JhI", + "outputId": "e4d8f16e-3b8f-4092-da00-348614ad3ea6" + }, + "execution_count": 22, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "43558755 €\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "**End of the code!**" + ], + "metadata": { + "id": "DSbvI3TY4S_b" + } + } + ] +} \ No newline at end of file diff --git a/docs/notebooks/Edwin_interarray_cplex_IEA.ipynb b/docs/notebooks/Edwin_interarray_cplex_IEA.ipynb new file mode 100644 index 0000000..3e30d60 --- /dev/null +++ b/docs/notebooks/Edwin_interarray_cplex_IEA.ipynb @@ -0,0 +1,1009 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "b3dc3e72-9621-4c61-b6bd-f6fc58294af3", + "metadata": {}, + "outputs": [], + "source": [ + "# Run Edwin with CPLEX solver" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f788f477-b4b4-405b-afd8-15dd5221ce68", + "metadata": {}, + "outputs": [], + "source": [ + "from ed_win.wind_farm_network import WindFarmNetwork, InterArrayDriver\n", + "from windIO.utils.yml_utils import load_yaml, Loader\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import xarray as xr\n", + "import psutil\n", + "import os" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5c69a05c-d986-4288-9a70-8978a7ff3ef8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total Memory: 15.69 GB\n", + "Available Memory: 5.63 GB\n", + "Physical CPUs: 4\n", + "Logical CPUs: 8\n", + "CPU Usage: 56.9%\n", + "CPU Frequency: 1405.0 MHz\n" + ] + } + ], + "source": [ + "# Get memory information\n", + "memory_info = psutil.virtual_memory()\n", + "\n", + "# Print available memory\n", + "print(f\"Total Memory: {memory_info.total / (1024 ** 3):.2f} GB\")\n", + "print(f\"Available Memory: {memory_info.available / (1024 ** 3):.2f} GB\")\n", + "\n", + "# Get CPU count and usage\n", + "cpu_count = psutil.cpu_count(logical=False)\n", + "cpu_count_logical = psutil.cpu_count(logical=True)\n", + "cpu_percent = psutil.cpu_percent(interval=1)\n", + "\n", + "# Get CPU frequency\n", + "cpu_freq = psutil.cpu_freq()\n", + "\n", + "# Print the information\n", + "print(f\"Physical CPUs: {cpu_count}\\nLogical CPUs: {cpu_count_logical}\\nCPU Usage: {cpu_percent}%\")\n", + "print(f\"CPU Frequency: {cpu_freq.current} MHz\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "df380228-55c6-4051-9a17-b3d52fab3369", + "metadata": {}, + "outputs": [], + "source": [ + "# Custom constructor for netCDF data\n", + "def includeBathymetryNetCDF(self, node):\n", + " filename = os.path.join(self._root, self.construct_scalar(node))\n", + " dataset = xr.open_dataset(filename)\n", + " bathymetry_data = {variable: list(dataset[variable].values.reshape(-1))\n", + " for variable in dataset.variables}\n", + " return bathymetry_data\n", + "\n", + "Loader.includeBathymetryNetCDF = includeBathymetryNetCDF\n", + "Loader.add_constructor('!includeBathymetryNetCDF',\n", + " Loader.includeBathymetryNetCDF)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "669b3568-ac3c-4e34-ba68-a94f3beff4e7", + "metadata": {}, + "outputs": [], + "source": [ + "# Regurlar layout\n", + "# Data extracted from CablingExperiments/load_data.py\n", + "substation_pos = np.asarray([[497620.7], [5730622.0]])\n", + "cable_costs = [206, 287, 406] # [€/m] Costs per distance for each cable type\n", + "turbines_per_cable = [3, 5, 7]\n", + "cross_section = [500, 1000, 1500]\n", + "BoundaryC = np.array(list(zip([484178.55, 500129.9, 497318.1,\n", + " 503163.37, 501266.5, 488951.0],\n", + " [5732482.8, 5737534.4, 5731880.24,\n", + " 5729155.3, 5715990.05, 5727940.])))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "adbae7b8-a026-41a5-bad6-bea48496e99d", + "metadata": {}, + "outputs": [], + "source": [ + "interarray_setting = {\n", + " 'solver_name': 'cplex', # choose between cplex, cbc,\n", + " 'gap': 0.002, # gab between best\n", + " 'timelimit': 2000, # seconds\n", + " 'other_settings': {\n", + " 'solver_io': 'python',\n", + " 'relax_boundary': True,\n", + " 'tee': True,\n", + " 'warmstart': True,\n", + " 'gateXings_constraint': True,\n", + " 'branching': True,\n", + " 'gates_limit': False,\n", + " 'timeMode': 'elapsed',\n", + " 'threads': len(psutil.Process().cpu_affinity()),\n", + " 'get_cbc_path': False,\n", + " 'cbc_solver_path': \"Provide path to the cbc.exe file\"\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "357cbfe5-223b-41b8-929f-b2f0f67c81a9", + "metadata": {}, + "outputs": [], + "source": [ + "cables = np.array([[cx, capacity, cost] for cx, capacity, cost in zip(cross_section, turbines_per_cable, cable_costs)])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "2af056bf-0178-4212-b431-d0ce0a70d5dd", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# Load Regular layout from YAML file\n", + "regular_system = load_yaml(\n", + " 'CablingExperiments/IEA37_Borssele_Regular_System.yaml', Loader)\n", + "\n", + "turbine_pos = np.array([\n", + " regular_system['wind_farm']['layouts']['initial_layout']['coordinates']['x'],\n", + " regular_system['wind_farm']['layouts']['initial_layout']['coordinates']['y']\n", + "])\n", + "\n", + "site_info = {'site_name': 'IEA-37 Regular',\n", + " 'handle': 'iea37reg',\n", + " 'boundary': BoundaryC\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "1f394116-1fbd-40fb-920a-15f3ae53fcec", + "metadata": {}, + "outputs": [], + "source": [ + "wfn = WindFarmNetwork(turbine_positions=turbine_pos,\n", + " substation_positions=substation_pos,\n", + " drivers=[InterArrayDriver(**interarray_setting)],\n", + " sequence=[0],\n", + " cables=cables,\n", + " site_info=site_info)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "77ae09e9-e836-4101-95f3-6c40daea26fb", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The solver is available\n", + "Solving \"IEA-37 Regular\", k = k\n", + "\n", + "Version identifier: 22.1.1.0 | 2022-11-27 | 9160aff4d\n", + "CPXPARAM_Read_DataCheck 1\n", + "CPXPARAM_TimeLimit 2000\n", + "CPXPARAM_MIP_Tolerances_MIPGap 0.002\n", + "1 of 1 MIP starts provided solutions.\n", + "MIP start 'm1' defined initial solution with objective 152083.9803.\n", + "Tried aggregator 1 time.\n", + "MIP Presolve eliminated 745 rows and 0 columns.\n", + "MIP Presolve modified 2293 coefficients.\n", + "Reduced MIP has 2929 rows, 1528 columns, and 11637 nonzeros.\n", + "Reduced MIP has 764 binaries, 764 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.05 sec. (11.81 ticks)\n", + "Probing time = 0.03 sec. (2.00 ticks)\n", + "Tried aggregator 1 time.\n", + "Detecting symmetries...\n", + "Reduced MIP has 2929 rows, 1528 columns, and 11637 nonzeros.\n", + "Reduced MIP has 764 binaries, 764 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.02 sec. (9.45 ticks)\n", + "Probing time = 0.02 sec. (1.85 ticks)\n", + "Clique table members: 1251.\n", + "MIP emphasis: balance optimality and feasibility.\n", + "MIP search method: dynamic search.\n", + "Parallel mode: deterministic, using up to 8 threads.\n", + "Root relaxation solution time = 0.06 sec. (28.13 ticks)\n", + "\n", + " Nodes Cuts/\n", + " Node Left Objective IInf Best Integer Best Bound ItCnt Gap\n", + "\n", + "* 0+ 0 152083.9803 0.0000 100.00%\n", + " 0 0 135593.2499 147 152083.9803 135593.2499 1173 10.84%\n", + "* 0+ 0 145767.5383 135593.2499 6.98%\n", + " 0 0 136222.3948 240 145767.5383 Cuts: 176 1385 6.55%\n", + " 0 0 136519.9505 218 145767.5383 Cuts: 88 1554 6.34%\n", + "* 0+ 0 144897.5898 136519.9505 5.78%\n", + " 0 0 136547.6125 202 144897.5898 Cuts: 52 1689 5.76%\n", + " 0 0 136596.6777 208 144897.5898 Cuts: 32 1787 5.73%\n", + " 0 0 136600.5962 232 144897.5898 Cuts: 19 1832 5.73%\n", + "Detecting symmetries...\n", + " 0 0 136625.1636 237 144897.5898 Cuts: 18 1922 5.71%\n", + " 0 0 136674.5042 232 144897.5898 Cuts: 27 2025 5.68%\n", + " 0 0 136687.0686 210 144897.5898 Cuts: 28 2079 5.67%\n", + " 0 0 136695.6644 206 144897.5898 Cuts: 16 2122 5.66%\n", + "* 0+ 0 141753.6497 136695.6644 3.57%\n", + "* 0+ 0 141753.6497 136695.6644 3.57%\n", + " 0 0 -1.00000e+75 0 141753.6497 136695.6644 2122 3.57%\n", + "Detecting symmetries...\n", + " 0 2 136695.6644 206 141753.6497 136695.6644 2122 3.57%\n", + "Elapsed time = 1.88 sec. (596.92 ticks, tree = 0.02 MB, solutions = 5)\n", + " 17 15 136782.8206 222 141753.6497 136730.0034 3358 3.54%\n", + " 145 86 137556.1594 224 141753.6497 136735.3061 10217 3.54%\n", + " 463 351 137004.2940 267 141753.6497 136799.5225 31138 3.49%\n", + " 864 662 137697.4447 224 141753.6497 136811.5631 50790 3.49%\n", + " 1352 1041 138315.4521 166 141753.6497 136841.2766 72439 3.47%\n", + " 1784 1451 138920.1981 164 141753.6497 136841.2766 99860 3.47%\n", + " 2306 1775 139737.2189 193 141753.6497 136843.0562 116354 3.46%\n", + " 2904 2325 137258.9988 241 141753.6497 136949.1377 146455 3.39%\n", + " 3375 2727 139794.3350 129 141753.6497 136993.2872 163974 3.36%\n", + "\n", + "Performing restart 1\n", + "\n", + "Repeating presolve.\n", + "Tried aggregator 1 time.\n", + "MIP Presolve eliminated 114 rows and 12 columns.\n", + "MIP Presolve modified 5 coefficients.\n", + "Reduced MIP has 2815 rows, 1516 columns, and 11131 nonzeros.\n", + "Reduced MIP has 758 binaries, 758 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.02 sec. (9.37 ticks)\n", + "Tried aggregator 1 time.\n", + "Reduced MIP has 2815 rows, 1516 columns, and 11131 nonzeros.\n", + "Reduced MIP has 758 binaries, 758 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.03 sec. (9.04 ticks)\n", + "Represolve time = 0.09 sec. (32.57 ticks)\n", + " 3385 0 136866.0253 254 141753.6497 Cuts: 82 177414 3.35%\n", + " 3385 0 136941.2447 253 141753.6497 Cuts: 103 177601 3.35%\n", + " 3385 0 137012.4732 276 141753.6497 Cuts: 80 177758 3.34%\n", + " 3385 0 137088.3684 260 141753.6497 Cuts: 75 177984 3.29%\n", + " 3385 0 137121.7797 264 141753.6497 Cuts: 69 178097 3.27%\n", + " 3385 0 137156.0567 264 141753.6497 Cuts: 79 178260 3.24%\n", + " 3385 0 137176.1213 270 141753.6497 Cuts: 112 178401 3.23%\n", + " 3385 0 137187.3219 269 141753.6497 Cuts: 83 178473 3.22%\n", + " 3385 0 137191.9461 278 141753.6497 Cuts: 61 178588 3.22%\n", + " 3385 0 137197.7382 318 141753.6497 Cuts: 79 178700 3.21%\n", + " 3385 0 137206.3795 305 141753.6497 Cuts: 45 178817 3.21%\n", + " 3385 0 137215.8525 267 141753.6497 Cuts: 35 178884 3.20%\n", + " 3385 0 137220.5709 300 141753.6497 Cuts: 43 178929 3.20%\n", + " 3385 0 137229.3622 308 141753.6497 Cuts: 39 179015 3.19%\n", + " 3385 0 137231.0846 298 141753.6497 Cuts: 48 179073 3.19%\n", + " 3385 0 137233.9995 294 141753.6497 Cuts: 27 179112 3.19%\n", + " 3385 0 137236.0027 278 141753.6497 Cuts: 33 179173 3.19%\n", + " 3385 0 137236.4351 277 141753.6497 Cuts: 31 179206 3.19%\n", + " 3385 2 137236.4351 277 141753.6497 137236.4351 179206 3.19%\n", + "Elapsed time = 15.34 sec. (5374.61 ticks, tree = 0.02 MB, solutions = 5)\n", + "* 3388+ 1 141725.4071 137247.3447 3.16%\n", + "* 3400+ 2 141002.8273 137248.4842 2.66%\n", + "* 3402+ 2 140702.3763 137248.4842 2.45%\n", + " 3525 107 139035.6481 148 140702.3763 137307.4044 187557 2.41%\n", + " 4320 705 140298.7242 168 140702.3763 137308.1940 230441 2.41%\n", + " 5683 1921 137970.3833 191 140702.3763 137377.8202 294361 2.36%\n", + " 6804 2834 139302.8190 172 140702.3763 137470.7044 353096 2.30%\n", + " 7973 3871 139236.1615 153 140702.3763 137519.3233 426695 2.26%\n", + " 8877 4696 138702.7969 184 140702.3763 137545.8966 474382 2.24%\n", + " 9519 5424 140436.9719 139 140702.3763 137570.7302 543857 2.23%\n", + " 10326 6048 138594.8318 126 140702.3763 137598.0587 602673 2.21%\n", + " 11013 6656 140424.0841 89 140702.3763 137621.0141 657332 2.19%\n", + "* 11319+ 6744 140696.7286 137631.5448 2.18%\n", + " 11674 7222 139632.5867 127 140696.7286 137647.1956 712559 2.17%\n", + "Elapsed time = 39.88 sec. (14920.07 ticks, tree = 12.44 MB, solutions = 12)\n", + " 12335 7586 140054.1347 204 140696.7286 137660.1595 758929 2.16%\n", + " 12803 8096 140651.1654 182 140696.7286 137669.1118 809889 2.15%\n", + " 13685 8733 139092.1803 209 140696.7286 137687.8702 872663 2.14%\n", + " 14252 9259 138505.6477 204 140696.7286 137698.7439 918407 2.13%\n", + " 14910 9984 cutoff 140696.7286 137717.6169 993394 2.12%\n", + " 15517 10465 140096.8391 170 140696.7286 137725.7062 1050340 2.11%\n", + " 15951 10815 138397.7968 168 140696.7286 137729.1629 1094342 2.11%\n", + " 16770 11344 138763.5310 211 140696.7286 137744.1031 1150300 2.10%\n", + " 17273 11908 138334.7198 193 140696.7286 137755.4623 1213728 2.09%\n", + " 17983 12589 140523.3626 138 140696.7286 137772.0146 1290317 2.08%\n", + "Elapsed time = 64.11 sec. (24470.24 ticks, tree = 22.12 MB, solutions = 12)\n", + " 18674 13119 140630.0531 208 140696.7286 137778.3922 1356241 2.07%\n", + " 19398 13675 140112.0437 111 140696.7286 137799.2990 1411301 2.06%\n", + " 20067 14110 139127.5703 203 140696.7286 137802.2017 1456418 2.06%\n", + " 20476 14793 139130.6864 204 140696.7286 137818.0506 1528437 2.05%\n", + " 21164 15067 138802.5886 260 140696.7286 137821.3804 1563105 2.04%\n", + " 22000 15898 139837.2713 174 140696.7286 137831.1866 1649503 2.04%\n", + " 22715 16371 138996.4184 170 140696.7286 137843.0072 1693865 2.03%\n", + " 23415 16940 139860.9714 214 140696.7286 137855.4757 1765715 2.02%\n", + " 24332 17740 138157.0825 214 140696.7286 137859.9905 1842228 2.02%\n", + " 25058 18506 139467.6673 189 140696.7286 137868.5210 1920043 2.01%\n", + "Elapsed time = 89.75 sec. (34022.20 ticks, tree = 32.99 MB, solutions = 12)\n", + " 25774 18969 140229.6027 246 140696.7286 137880.0935 1969315 2.00%\n", + " 26487 19412 138927.0458 154 140696.7286 137889.5902 2025955 2.00%\n", + "* 27007+19728 140696.7282 137892.7192 1.99%\n", + " 27434 20355 139381.6286 165 140696.7282 137894.5288 2117666 1.99%\n", + " 28355 20939 140423.6033 169 140696.7282 137904.5800 2158351 1.98%\n", + " 29116 21470 139339.0037 184 140696.7282 137912.3388 2184260 1.98%\n", + " 29625 22393 139601.4236 158 140696.7282 137922.2057 2251326 1.97%\n", + " 30543 22595 138853.8236 216 140696.7282 137925.8612 2267885 1.97%\n", + " 31649 23834 139304.4066 240 140696.7282 137928.7711 2330733 1.97%\n", + " 32721 24271 139297.6005 248 140696.7282 137937.5603 2350284 1.96%\n", + " 33570 25582 139333.7270 193 140696.7282 137947.2915 2429029 1.95%\n", + "Elapsed time = 115.11 sec. (43568.00 ticks, tree = 134.60 MB, solutions = 13)\n", + " 34497 25770 139709.7002 247 140696.7282 137954.4405 2445489 1.95%\n", + " 35392 26815 139530.5578 219 140696.7282 137959.5371 2505018 1.95%\n", + " 36149 27518 140314.7790 215 140696.7282 137963.5083 2545440 1.94%\n", + " 37028 27900 139506.8700 195 140696.7282 137968.3093 2570347 1.94%\n", + " 37929 29038 140183.3080 196 140696.7282 137974.7968 2647762 1.93%\n", + " 38946 29975 139576.3726 137 140696.7282 137979.4734 2706883 1.93%\n", + " 39915 30392 138551.1433 236 140696.7282 137984.6962 2730741 1.93%\n", + " 40654 31211 139326.5972 224 140696.7282 137988.4625 2778019 1.92%\n", + " 41558 31616 140146.2070 211 140696.7282 137994.2367 2816456 1.92%\n", + " 42504 32249 139630.3515 198 140696.7282 138006.0339 2860954 1.91%\n", + "Elapsed time = 139.77 sec. (53114.54 ticks, tree = 264.64 MB, solutions = 13)\n", + " 43503 32949 139025.4542 202 140696.7282 138010.5692 2905150 1.91%\n", + " 44352 34544 cutoff 140696.7282 138018.1184 2998931 1.90%\n", + " 45142 35117 140379.4786 191 140696.7282 138022.8355 3035161 1.90%\n", + " 45679 35159 140133.1689 226 140696.7282 138026.8026 3038871 1.90%\n", + " 46491 35912 138676.4763 226 140696.7282 138031.7843 3089596 1.89%\n", + " 47634 36343 138782.9950 176 140696.7282 138036.1854 3121755 1.89%\n", + " 48643 37267 cutoff 140696.7282 138040.0879 3173917 1.89%\n", + " 49481 38158 140427.8071 170 140696.7282 138047.4273 3220970 1.88%\n", + " 50294 39123 140550.9340 199 140696.7282 138052.8595 3296666 1.88%\n", + " 51287 39827 139824.8136 205 140696.7282 138055.8721 3343344 1.88%\n", + "Elapsed time = 164.50 sec. (62661.06 ticks, tree = 415.23 MB, solutions = 13)\n", + "* 51994 40171 integral 0 139479.8963 138061.6256 3358949 1.02%\n", + "* 52183+20923 139479.8960 138062.5334 1.02%\n", + " 52190 20886 138824.3782 204 139479.8960 138062.5334 3392031 1.02%\n", + " 52989 21294 139339.1882 246 139479.8960 138070.9916 3443935 1.01%\n", + " 53727 21363 138621.3537 226 139479.8960 138075.8953 3450284 1.01%\n", + " 54319 22057 138567.1334 234 139479.8960 138081.3396 3503191 1.00%\n", + " 55038 22307 139466.4828 49 139479.8960 138083.9542 3529438 1.00%\n", + " 55749 22802 138949.8917 213 139479.8960 138092.1589 3586745 0.99%\n", + " 56494 23502 139097.6960 143 139479.8960 138100.6520 3650322 0.99%\n", + " 57091 23960 138632.8470 208 139479.8960 138106.5395 3687838 0.98%\n", + " 57690 24542 138552.0533 192 139479.8960 138112.4368 3753611 0.98%\n", + " 58326 24715 138820.0908 91 139479.8960 138115.7013 3773594 0.98%\n", + "Elapsed time = 189.58 sec. (72211.52 ticks, tree = 286.75 MB, solutions = 15)\n", + " 59136 25140 138651.9251 142 139479.8960 138121.5854 3815098 0.97%\n", + " 59708 25599 138529.5375 182 139479.8960 138124.4230 3865300 0.97%\n", + " 60158 26273 138602.4789 212 139479.8960 138133.8644 3922565 0.97%\n", + " 60612 26601 cutoff 139479.8960 138139.2098 3962979 0.96%\n", + " 61304 26793 138553.6617 224 139479.8960 138143.1262 3990041 0.96%\n", + " 62004 27332 139241.1449 193 139479.8960 138149.2275 4040685 0.95%\n", + " 62630 27908 139155.0100 232 139479.8960 138152.5747 4095198 0.95%\n", + " 63248 28304 138791.4041 217 139479.8960 138156.9435 4134091 0.95%\n", + " 63900 28553 138950.6090 213 139479.8960 138159.3532 4162574 0.95%\n", + " 64595 28818 139134.8243 156 139479.8960 138165.1730 4200916 0.94%\n", + "Elapsed time = 214.83 sec. (81767.54 ticks, tree = 365.13 MB, solutions = 15)\n", + " 65377 29688 139334.3712 171 139479.8960 138170.3885 4283757 0.94%\n", + " 66008 30443 138684.1962 70 139479.8960 138175.4939 4347957 0.94%\n", + " 66668 30601 139225.1447 228 139479.8960 138177.8887 4366303 0.93%\n", + " 67264 30791 139152.7416 117 139479.8960 138184.3746 4394051 0.93%\n", + " 67648 31195 139332.0931 217 139479.8960 138184.3746 4432063 0.93%\n", + "\n", + "Performing restart 2\n", + "\n", + "Repeating presolve.\n", + "Tried aggregator 1 time.\n", + "MIP Presolve eliminated 257 rows and 76 columns.\n", + "MIP Presolve modified 78 coefficients.\n", + "Reduced MIP has 2558 rows, 1440 columns, and 9705 nonzeros.\n", + "Reduced MIP has 720 binaries, 720 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.03 sec. (8.45 ticks)\n", + "Tried aggregator 1 time.\n", + "Reduced MIP has 2558 rows, 1440 columns, and 9705 nonzeros.\n", + "Reduced MIP has 720 binaries, 720 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.03 sec. (7.52 ticks)\n", + "Represolve time = 0.58 sec. (67.16 ticks)\n", + " 67659 0 137326.7891 308 139479.8960 Cuts: 36 4484080 0.93%\n", + " 67659 0 137332.0278 299 139479.8960 Cuts: 25 4484172 0.93%\n", + " 67659 0 137334.3430 316 139479.8960 Cuts: 26 4484243 0.93%\n", + " 67659 0 137336.4665 299 139479.8960 Cuts: 26 4484323 0.93%\n", + " 67659 0 137338.7583 318 139479.8960 Cuts: 36 4484383 0.93%\n", + " 67659 0 137339.5557 310 139479.8960 Cuts: 26 4484460 0.93%\n", + " 67659 0 137340.2588 309 139479.8960 Cuts: 28 4484521 0.93%\n", + " 67659 0 137341.3530 330 139479.8960 Cuts: 39 4484576 0.93%\n", + " 67659 0 137343.2636 303 139479.8960 Cuts: 39 4484652 0.93%\n", + " 67659 0 137343.9022 301 139479.8960 Cuts: 27 4484678 0.93%\n", + "\n", + "Repeating presolve.\n", + "Tried aggregator 2 times.\n", + "MIP Presolve eliminated 223 rows and 48 columns.\n", + "MIP Presolve modified 84 coefficients.\n", + "Aggregator did 1 substitutions.\n", + "Reduced MIP has 2334 rows, 1391 columns, and 8641 nonzeros.\n", + "Reduced MIP has 695 binaries, 696 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.01 sec. (7.68 ticks)\n", + "Tried aggregator 1 time.\n", + "Reduced MIP has 2334 rows, 1391 columns, and 8641 nonzeros.\n", + "Reduced MIP has 695 binaries, 696 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.02 sec. (6.45 ticks)\n", + "Represolve time = 0.20 sec. (64.98 ticks)\n", + " 67659 0 137344.9447 329 139479.8960 Cuts: 25 4486725 0.93%\n", + " 67659 0 137345.3224 328 139479.8960 Cuts: 21 4486760 0.93%\n", + " 67659 0 137345.4571 325 139479.8960 Cuts: 9 4486785 0.93%\n", + " 67659 0 137345.5482 326 139479.8960 Cuts: 17 4486802 0.93%\n", + " 67659 0 137345.6182 327 139479.8960 Cuts: 12 4486835 0.93%\n", + " 67659 0 137345.7791 327 139479.8960 Cuts: 3 4486858 0.93%\n", + " 67659 0 137346.1531 333 139479.8960 Cuts: 19 4486920 0.93%\n", + " 67659 2 137346.1531 333 139479.8960 138188.8144 4486920 0.93%\n", + " 67672 10 138087.6736 158 139479.8960 138188.8144 4488930 0.93%\n", + " 67741 61 139169.6305 162 139479.8960 138188.8144 4499013 0.93%\n", + " 67866 135 137981.4702 239 139479.8960 138188.8144 4506626 0.93%\n", + " 69844 1255 138691.8326 194 139479.8960 138188.8144 4580126 0.93%\n", + "Elapsed time = 256.84 sec. (97761.18 ticks, tree = 16.92 MB, solutions = 15)\n", + " 73993 4463 138707.6634 179 139479.8960 138188.8144 4777965 0.93%\n", + " 78612 7767 139327.7516 162 139479.8960 138188.8144 4991261 0.93%\n", + " 82584 10933 138996.8295 201 139479.8960 138188.8144 5221878 0.93%\n", + " 87141 14054 139193.5440 188 139479.8960 138188.8144 5432003 0.93%\n", + " 91285 17137 138977.5948 170 139479.8960 138188.8144 5659713 0.93%\n", + " 94975 19556 138157.3380 211 139479.8960 138188.8144 5868236 0.93%\n", + " 98380 22062 138426.9526 242 139479.8960 138188.8144 6081711 0.93%\n", + " 101876 24552 138697.2380 202 139479.8960 138188.8144 6300298 0.93%\n", + " 105594 27062 139458.9561 192 139479.8960 138210.1945 6529907 0.91%\n", + " 109101 29026 138965.0687 174 139479.8960 138233.8582 6711346 0.89%\n", + "Elapsed time = 336.69 sec. (135919.40 ticks, tree = 458.27 MB, solutions = 15)\n", + " 112423 31140 138909.0904 215 139479.8960 138258.3306 6912046 0.88%\n", + " 115317 33308 cutoff 139479.8960 138278.4541 7142643 0.86%\n", + " 117984 34771 138833.7289 164 139479.8960 138301.2370 7309759 0.85%\n", + " 120874 36837 139288.2895 180 139479.8960 138318.6838 7543772 0.83%\n", + " 123480 38211 139114.8413 163 139479.8960 138332.6219 7694738 0.82%\n", + " 126286 39867 139239.7496 102 139479.8960 138346.3952 7895538 0.81%\n", + " 128799 41267 139282.8207 192 139479.8960 138363.2465 8068702 0.80%\n", + " 131383 43003 139167.5933 182 139479.8960 138377.1571 8303905 0.79%\n", + " 133733 44002 138981.6431 224 139479.8960 138394.5693 8429336 0.78%\n", + " 136347 45687 139352.6016 181 139479.8960 138407.6827 8642303 0.77%\n", + "Elapsed time = 409.78 sec. (174081.46 ticks, tree = 761.52 MB, solutions = 15)\n", + " 138553 47023 139429.5281 205 139479.8960 138419.7899 8836511 0.76%\n", + " 141196 48635 138980.6053 240 139479.8960 138431.8432 9034375 0.75%\n", + " 143792 49677 139340.8321 193 139479.8960 138445.6216 9181302 0.74%\n", + " 146015 51308 139391.2798 157 139479.8960 138459.0305 9395690 0.73%\n", + " 148488 52738 139010.0401 260 139479.8960 138471.0183 9586929 0.72%\n", + " 151029 53812 138898.3859 209 139479.8960 138481.0350 9728667 0.72%\n", + " 153218 55317 138802.4135 155 139479.8960 138492.7650 9935910 0.71%\n", + " 155389 56133 138840.8217 212 139479.8960 138503.1618 10096389 0.70%\n", + " 157617 57468 139163.0328 189 139479.8960 138513.3236 10309993 0.69%\n", + " 159862 58320 139371.8455 69 139479.8960 138523.2080 10444218 0.69%\n", + "Elapsed time = 484.41 sec. (212247.77 ticks, tree = 981.40 MB, solutions = 15)\n", + " 162209 59584 139305.4635 176 139479.8960 138531.6442 10652802 0.68%\n", + " 164429 60967 138716.0548 195 139479.8960 138540.7346 10850420 0.67%\n", + " 166475 61821 139269.6668 207 139479.8960 138549.5181 10989338 0.67%\n", + " 168735 63096 139335.9320 239 139479.8960 138557.5430 11184830 0.66%\n", + " 170849 63986 138698.5692 274 139479.8960 138565.2987 11328289 0.66%\n", + " 172707 64822 cutoff 139479.8960 138572.7280 11486045 0.65%\n", + " 174727 65626 139044.8755 218 139479.8960 138581.0948 11632866 0.64%\n", + " 176962 66786 139008.7893 172 139479.8960 138589.4294 11840280 0.64%\n", + " 178632 67487 139266.2378 243 139479.8960 138598.6611 12008463 0.63%\n", + " 180589 67930 139174.6258 167 139479.8960 138606.5697 12108202 0.63%\n", + "Elapsed time = 560.75 sec. (250438.71 ticks, tree = 1151.46 MB, solutions = 15)\n", + " 182726 69021 139303.0773 192 139479.8960 138614.8902 12309902 0.62%\n", + " 184745 70108 139288.5668 282 139479.8960 138622.4431 12520449 0.61%\n", + " 186571 70725 139300.6241 111 139479.8960 138629.6481 12646064 0.61%\n", + " 188750 71708 139383.6844 159 139479.8960 138636.8209 12832456 0.60%\n", + " 190765 72431 cutoff 139479.8960 138644.2498 12990579 0.60%\n", + " 192991 73102 139437.7422 194 139479.8960 138651.3848 13145407 0.59%\n", + " 194807 73844 139426.6198 187 139479.8960 138660.1012 13294206 0.59%\n", + " 197097 74614 139090.3060 242 139479.8960 138667.8491 13456054 0.58%\n", + " 199090 75503 139417.3802 239 139479.8960 138674.0575 13649817 0.58%\n", + " 201236 76165 139027.3363 25 139479.8960 138682.6897 13797698 0.57%\n", + "Elapsed time = 637.81 sec. (288600.70 ticks, tree = 1300.92 MB, solutions = 15)\n", + " 202926 76993 cutoff 139479.8960 138690.1646 13991007 0.57%\n", + " 204829 77701 cutoff 139479.8960 138697.4968 14186041 0.56%\n", + " 207055 78254 cutoff 139479.8960 138703.8414 14310833 0.56%\n", + " 209175 79149 139075.4258 241 139479.8960 138710.4749 14496772 0.55%\n", + " 211009 79829 139390.1870 148 139479.8960 138719.3620 14651035 0.55%\n", + " 213081 80461 139095.5671 200 139479.8960 138725.1929 14812724 0.54%\n", + " 215416 81334 139045.2091 28 139479.8960 138731.6794 15006488 0.54%\n", + " 217476 82010 cutoff 139479.8960 138738.2190 15158739 0.53%\n", + " 219540 82461 cutoff 139479.8960 138744.4025 15303358 0.53%\n", + " 221514 83074 cutoff 139479.8960 138751.4296 15468869 0.52%\n", + "Elapsed time = 714.36 sec. (326763.12 ticks, tree = 1423.05 MB, solutions = 15)\n", + " 223568 83802 cutoff 139479.8960 138757.5943 15630042 0.52%\n", + " 225701 84410 139182.4561 246 139479.8960 138763.6623 15796186 0.51%\n", + " 227699 85062 139438.6174 226 139479.8960 138769.5820 15984273 0.51%\n", + " 229657 85703 139447.8810 216 139479.8960 138777.4873 16152570 0.50%\n", + " 231670 86257 139471.1280 226 139479.8960 138783.4468 16323481 0.50%\n", + " 233850 86896 139421.4402 183 139479.8960 138789.3501 16511944 0.50%\n", + " 235962 87370 139291.0815 156 139479.8960 138794.8074 16645101 0.49%\n", + " 237807 87988 cutoff 139479.8960 138800.8662 16809771 0.49%\n", + " 239863 88490 139205.1896 150 139479.8960 138806.8977 16990505 0.48%\n", + " 242032 89007 139138.4123 46 139479.8960 138812.8098 17138073 0.48%\n", + "Elapsed time = 793.81 sec. (364929.68 ticks, tree = 1526.21 MB, solutions = 15)\n", + " 244062 89844 139474.9180 226 139479.8960 138819.2483 17384931 0.47%\n", + " 245992 90399 139135.3596 223 139479.8960 138824.2954 17527864 0.47%\n", + " 247936 90597 cutoff 139479.8960 138829.5422 17621236 0.47%\n", + " 250164 91233 139222.4479 227 139479.8960 138835.7666 17817249 0.46%\n", + " 252330 91941 cutoff 139479.8960 138840.8481 18002110 0.46%\n", + " 254701 92872 cutoff 139479.8960 138846.5133 18206115 0.45%\n", + " 256523 93288 139406.6871 206 139479.8960 138850.7410 18328537 0.45%\n", + " 258768 93693 cutoff 139479.8960 138856.1086 18463712 0.45%\n", + " 260778 94334 cutoff 139479.8960 138860.9489 18693757 0.44%\n", + " 263087 94903 139153.8365 161 139479.8960 138865.9433 18842609 0.44%\n", + "Elapsed time = 877.02 sec. (403098.85 ticks, tree = 1623.19 MB, solutions = 15)\n", + " 265162 95657 cutoff 139479.8960 138870.6109 19042270 0.44%\n", + " 267229 96273 139203.9025 24 139479.8960 138875.8424 19207954 0.43%\n", + " 269520 96843 cutoff 139479.8960 138880.4069 19375437 0.43%\n", + " 271875 97344 cutoff 139479.8960 138885.2228 19520316 0.43%\n", + " 273983 97874 cutoff 139479.8960 138889.9811 19672791 0.42%\n", + "*275838+98301 139479.8952 138894.9321 0.42%\n", + " 276355 98555 139275.9428 157 139479.8952 138895.9433 19890155 0.42%\n", + " 278358 99250 139363.4259 179 139479.8952 138900.5008 20082604 0.42%\n", + " 280447 99389 138957.0657 212 139479.8952 138905.0855 20205458 0.41%\n", + " 282607 99783 139289.5432 254 139479.8952 138909.1579 20339656 0.41%\n", + " 284847 100222 cutoff 139479.8952 138913.5291 20506064 0.41%\n", + "Elapsed time = 958.48 sec. (441276.46 ticks, tree = 1710.94 MB, solutions = 16)\n", + " 287133 100647 139157.7786 216 139479.8952 138919.1070 20680101 0.40%\n", + " 289019 101171 139235.6286 170 139479.8952 138923.9732 20923226 0.40%\n", + " 291274 101494 139442.5581 87 139479.8952 138928.6042 21061895 0.40%\n", + " 293449 101894 cutoff 139479.8952 138932.5677 21221135 0.39%\n", + " 295869 102423 139187.6487 197 139479.8952 138936.1574 21422684 0.39%\n", + " 298143 102758 139324.5127 229 139479.8952 138940.9775 21552303 0.39%\n", + " 300508 103220 cutoff 139479.8952 138945.7361 21739562 0.38%\n", + " 302499 103501 cutoff 139479.8952 138949.9486 21853641 0.38%\n", + " 304975 104032 cutoff 139479.8952 138954.6578 22102262 0.38%\n", + " 307262 104417 139052.2693 257 139479.8952 138959.1392 22216390 0.37%\n", + "Elapsed time = 1037.78 sec. (479432.10 ticks, tree = 1788.29 MB, solutions = 16)\n", + " 309750 104893 139044.2341 139 139479.8952 138962.5465 22398954 0.37%\n", + " 312079 105329 cutoff 139479.8952 138966.4006 22561970 0.37%\n", + " 314192 105727 139206.4349 8 139479.8952 138970.4915 22783659 0.37%\n", + " 316485 106021 cutoff 139479.8952 138975.2004 22895265 0.36%\n", + " 318808 106457 cutoff 139479.8952 138978.6887 23125221 0.36%\n", + " 321151 106748 139153.2552 167 139479.8952 138982.9168 23282141 0.36%\n", + " 323366 106805 cutoff 139479.8952 138986.8583 23441799 0.35%\n", + " 325501 106922 139212.8241 74 139479.8952 138990.5166 23600698 0.35%\n", + " 327744 107064 cutoff 139479.8952 138995.0202 23815270 0.35%\n", + " 329989 106921 cutoff 139479.8952 138999.8152 23929191 0.34%\n", + "Elapsed time = 1121.23 sec. (517591.15 ticks, tree = 1814.61 MB, solutions = 16)\n", + " 332214 106981 cutoff 139479.8952 139003.0229 24112638 0.34%\n", + " 334597 107045 cutoff 139479.8952 139008.0900 24296834 0.34%\n", + " 336520 107279 139216.5740 138 139479.8952 139012.9338 24467007 0.33%\n", + " 338864 107477 139472.0738 104 139479.8952 139017.3210 24647105 0.33%\n", + " 341279 107646 cutoff 139479.8952 139021.6868 24780891 0.33%\n", + " 343606 107999 cutoff 139479.8952 139025.1432 25006622 0.33%\n", + " 346179 108299 139314.8482 172 139479.8952 139029.4512 25161089 0.32%\n", + " 348402 108438 cutoff 139479.8952 139033.1554 25339904 0.32%\n", + " 350582 108526 cutoff 139479.8952 139035.6206 25434957 0.32%\n", + " 352969 108583 cutoff 139479.8952 139039.7790 25617238 0.32%\n", + "Elapsed time = 1204.69 sec. (555755.46 ticks, tree = 1825.26 MB, solutions = 16)\n", + " 355403 108737 139437.9148 178 139479.8952 139043.4334 25782086 0.31%\n", + " 357798 108726 cutoff 139479.8952 139047.4397 25959569 0.31%\n", + " 360096 108662 cutoff 139479.8952 139051.7871 26191352 0.31%\n", + " 362505 108553 139433.1519 154 139479.8952 139055.5998 26345324 0.30%\n", + " 364493 108423 139227.3891 136 139479.8952 139060.2099 26498154 0.30%\n", + " 366901 108373 139431.7463 172 139479.8952 139064.2674 26594204 0.30%\n", + " 369401 108310 cutoff 139479.8952 139068.9212 26769359 0.29%\n", + " 371747 108207 139190.9080 210 139479.8952 139072.8635 26949985 0.29%\n", + " 374264 108012 139176.7029 42 139479.8952 139077.1102 27180604 0.29%\n", + " 376676 107880 cutoff 139479.8952 139081.5271 27356787 0.29%\n", + "Elapsed time = 1285.58 sec. (593917.25 ticks, tree = 1792.57 MB, solutions = 16)\n", + " 378828 107578 139386.3853 188 139479.8952 139085.7657 27495156 0.28%\n", + " 381299 107302 139322.8342 86 139479.8952 139090.2769 27620488 0.28%\n", + " 383807 106992 139391.6192 241 139479.8952 139094.2738 27833436 0.28%\n", + " 386327 106848 cutoff 139479.8952 139098.6992 27919080 0.27%\n", + " 388743 106377 139422.4463 175 139479.8952 139103.4325 28146994 0.27%\n", + " 391341 105981 139370.2121 164 139479.8952 139107.6442 28319031 0.27%\n", + " 393532 105525 cutoff 139479.8952 139112.4222 28464265 0.26%\n", + " 396084 105231 cutoff 139479.8952 139117.1054 28615763 0.26%\n", + " 398582 104771 139272.8182 28 139479.8952 139121.4574 28779871 0.26%\n", + " 401149 104412 cutoff 139479.8952 139126.2569 28901557 0.25%\n", + "Elapsed time = 1375.88 sec. (632084.50 ticks, tree = 1710.74 MB, solutions = 16)\n", + " 403795 104156 cutoff 139479.8952 139130.3658 29071598 0.25%\n", + " 406416 103944 139349.8049 140 139479.8952 139135.7621 29287895 0.25%\n", + " 408815 103922 cutoff 139479.8952 139139.9936 29425351 0.24%\n", + " 411454 104005 cutoff 139479.8952 139143.5129 29624049 0.24%\n", + " 414064 103815 139394.7834 178 139479.8952 139146.7777 29807207 0.24%\n", + " 416693 103551 cutoff 139479.8952 139149.5520 29970136 0.24%\n", + " 419264 103163 cutoff 139479.8952 139152.1039 30115414 0.24%\n", + " 421837 102614 cutoff 139479.8952 139156.2273 30299282 0.23%\n", + " 424324 102340 139375.7202 161 139479.8952 139159.7339 30433235 0.23%\n", + " 426783 101791 cutoff 139479.8952 139164.2372 30645499 0.23%\n", + "Elapsed time = 1467.69 sec. (670241.03 ticks, tree = 1634.90 MB, solutions = 16)\n", + " 429459 101555 cutoff 139479.8952 139168.1912 30827778 0.22%\n", + " 432086 100936 139266.7365 177 139479.8952 139172.2224 31002784 0.22%\n", + " 434564 100484 cutoff 139479.8952 139176.1246 31127531 0.22%\n", + " 437109 100026 cutoff 139479.8952 139179.1067 31313746 0.22%\n", + " 439648 99396 139195.7482 202 139479.8952 139183.2252 31491124 0.21%\n", + " 442028 98673 139200.4415 189 139479.8952 139186.4243 31688819 0.21%\n", + "\n", + "GUB cover cuts applied: 15\n", + "Cover cuts applied: 11\n", + "Flow cuts applied: 48\n", + "Mixed integer rounding cuts applied: 917\n", + "Zero-half cuts applied: 42\n", + "Lift and project cuts applied: 70\n", + "Gomory fractional cuts applied: 12\n", + "\n", + "Root node processing (before b&c):\n", + " Real time = 1.88 sec. (595.80 ticks)\n", + "Parallel b&c, 8 threads:\n", + " Real time = 1524.38 sec. (695894.48 ticks)\n", + " Sync time (average) = 93.19 sec.\n", + " Wait time (average) = 0.13 sec.\n", + " ------------\n", + "Total (root+branch&cut) = 1526.25 sec. (696490.28 ticks)\n" + ] + } + ], + "source": [ + "cost, state = wfn.design()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "47048032-60a0-488f-98e0-87b6a98e3451", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "wfn.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "d4892305-d1ae-422c-8243-484c74616a45", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "40586828 €\n" + ] + } + ], + "source": [ + "print(round(cost), ' €')" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "b36c598c-9f65-4b06-9aa6-ce11f0fd6833", + "metadata": {}, + "outputs": [], + "source": [ + "# Load Irregular layout from YAML file\n", + "irregular_system = load_yaml(\n", + " 'CablingExperiments/IEA37_Borssele_irregular_System.yaml', Loader)\n", + "\n", + "turbine_pos = np.array([\n", + " irregular_system['wind_farm']['layouts']['initial_layout']['coordinates']['x'],\n", + " irregular_system['wind_farm']['layouts']['initial_layout']['coordinates']['y']\n", + "])\n", + "\n", + "site_info = {'site_name': 'IEA-37 Irregular',\n", + " 'handle': 'iea37irreg',\n", + " 'boundary': BoundaryC\n", + " }" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "ccdf5efa-0c6e-452e-a1f2-071eb9ab79b7", + "metadata": {}, + "outputs": [], + "source": [ + "wfn = WindFarmNetwork(turbine_positions=turbine_pos,\n", + " substation_positions=substation_pos,\n", + " drivers=[InterArrayDriver(**interarray_setting)],\n", + " sequence=[0],\n", + " cables=cables,\n", + " site_info=site_info)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "8ea63e17-a575-43f3-ba32-0e67833d890c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The solver is available\n", + "<edge_crossing> discarding ('bn', 'Z'): would cross [('M', 'bo')]\n", + "Solving \"IEA-37 Irregular\", k = k\n", + "\n", + "Version identifier: 22.1.1.0 | 2022-11-27 | 9160aff4d\n", + "CPXPARAM_Read_DataCheck 1\n", + "CPXPARAM_TimeLimit 2000\n", + "CPXPARAM_MIP_Tolerances_MIPGap 0.002\n", + "1 of 1 MIP starts provided solutions.\n", + "MIP start 'm1' defined initial solution with objective 148100.6920.\n", + "Tried aggregator 1 time.\n", + "MIP Presolve eliminated 502 rows and 0 columns.\n", + "MIP Presolve modified 1587 coefficients.\n", + "Reduced MIP has 2246 rows, 1144 columns, and 8234 nonzeros.\n", + "Reduced MIP has 572 binaries, 572 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.02 sec. (8.33 ticks)\n", + "Probing time = 0.00 sec. (2.69 ticks)\n", + "Tried aggregator 1 time.\n", + "Detecting symmetries...\n", + "Reduced MIP has 2246 rows, 1144 columns, and 8234 nonzeros.\n", + "Reduced MIP has 572 binaries, 572 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.01 sec. (6.52 ticks)\n", + "Probing time = 0.00 sec. (2.65 ticks)\n", + "Clique table members: 952.\n", + "MIP emphasis: balance optimality and feasibility.\n", + "MIP search method: dynamic search.\n", + "Parallel mode: deterministic, using up to 8 threads.\n", + "Root relaxation solution time = 0.03 sec. (16.76 ticks)\n", + "\n", + " Nodes Cuts/\n", + " Node Left Objective IInf Best Integer Best Bound ItCnt Gap\n", + "\n", + "* 0+ 0 148100.6920 0.0000 100.00%\n", + " 0 0 127486.1477 160 148100.6920 127486.1477 874 13.92%\n", + " 0 0 129348.0986 198 148100.6920 Cuts: 148 997 12.66%\n", + "* 0+ 0 139866.1910 129348.0986 7.52%\n", + " 0 0 129767.3967 241 139866.1910 Cuts: 55 1092 7.22%\n", + " 0 0 129902.2666 246 139866.1910 Cuts: 34 1154 7.12%\n", + " 0 0 129973.0537 252 139866.1910 Cuts: 21 1199 7.07%\n", + " 0 0 130061.2400 247 139866.1910 Cuts: 27 1255 7.01%\n", + "Detecting symmetries...\n", + " 0 0 130134.6001 284 139866.1910 Cuts: 33 1319 6.96%\n", + " 0 0 130169.5927 265 139866.1910 Cuts: 21 1358 6.93%\n", + " 0 0 130285.0161 241 139866.1910 Cuts: 24 1412 6.85%\n", + " 0 0 130310.0106 291 139866.1910 Cuts: 26 1460 6.83%\n", + " 0 0 130370.7102 284 139866.1910 Cuts: 19 1504 6.79%\n", + " 0 0 130405.9701 288 139866.1910 Cuts: 34 1539 6.76%\n", + " 0 0 130415.0216 276 139866.1910 Cuts: 15 1572 6.76%\n", + " 0 0 130452.6261 260 139866.1910 Cuts: 17 1607 6.73%\n", + " 0 0 130464.6545 290 139866.1910 Cuts: 20 1630 6.72%\n", + " 0 0 130513.8333 274 139866.1910 Cuts: 14 1672 6.69%\n", + " 0 0 130526.4154 280 139866.1910 Cuts: 7 1693 6.68%\n", + "Detecting symmetries...\n", + " 0 2 130526.4154 280 139866.1910 130526.4154 1693 6.68%\n", + "Elapsed time = 0.80 sec. (448.52 ticks, tree = 0.02 MB, solutions = 2)\n", + "* 15+ 3 139646.7016 130526.4252 6.53%\n", + " 326 269 139578.7151 127 139646.7016 130530.2733 14078 6.53%\n", + " 825 687 138193.0364 127 139646.7016 130538.8231 33964 6.52%\n", + " 1446 1088 136086.7184 103 139646.7016 130617.4550 55232 6.47%\n", + " 2175 1641 135014.7109 196 139646.7016 130626.5748 73277 6.46%\n", + " 2980 2407 135757.6505 233 139646.7016 130694.1853 94686 6.41%\n", + "\n", + "Performing restart 1\n", + "\n", + "Repeating presolve.\n", + "Tried aggregator 1 time.\n", + "Reduced MIP has 2246 rows, 1144 columns, and 8234 nonzeros.\n", + "Reduced MIP has 572 binaries, 572 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.01 sec. (5.60 ticks)\n", + "Tried aggregator 1 time.\n", + "Reduced MIP has 2246 rows, 1144 columns, and 8234 nonzeros.\n", + "Reduced MIP has 572 binaries, 572 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.00 sec. (6.69 ticks)\n", + "Represolve time = 0.06 sec. (26.27 ticks)\n", + " 3361 0 130680.8858 271 139646.7016 Cuts: 78 115299 6.41%\n", + " 3361 0 130807.8763 283 139646.7016 Cuts: 53 115389 6.33%\n", + " 3361 0 130941.2961 291 139646.7016 Cuts: 63 115513 6.23%\n", + " 3361 0 130997.9837 269 139646.7016 Cuts: 97 115599 6.19%\n", + " 3361 0 131123.1503 296 139646.7016 Cuts: 47 115704 6.10%\n", + " 3361 0 131170.1957 270 139646.7016 Cuts: 53 115791 6.07%\n", + " 3361 0 131213.8547 319 139646.7016 Cuts: 58 115882 6.04%\n", + " 3361 0 131256.2923 309 139646.7016 Cuts: 58 115976 6.01%\n", + " 3361 0 131322.6566 289 139646.7016 Cuts: 58 116074 5.96%\n", + " 3361 0 131381.3655 306 139646.7016 Cuts: 65 116147 5.92%\n", + " 3361 0 131531.1260 284 139646.7016 Cuts: 39 116235 5.81%\n", + " 3361 0 131641.6494 295 139646.7016 Cuts: 55 116331 5.73%\n", + " 3361 0 131723.3866 280 139646.7016 Cuts: 32 116395 5.67%\n", + " 3361 0 131744.0263 283 139646.7016 Cuts: 27 116447 5.66%\n", + " 3361 0 131792.7277 290 139646.7016 Cuts: 36 116519 5.62%\n", + " 3361 0 131823.4906 275 139646.7016 Cuts: 37 116574 5.60%\n", + " 3361 0 131857.2793 316 139646.7016 Cuts: 33 116637 5.58%\n", + " 3361 0 131878.6164 307 139646.7016 Cuts: 23 116697 5.56%\n", + " 3361 0 131906.9392 293 139646.7016 Cuts: 38 116767 5.54%\n", + " 3361 0 131915.4408 296 139646.7016 Cuts: 41 116801 5.54%\n", + " 3361 0 131927.9397 294 139646.7016 Cuts: 27 116839 5.53%\n", + " 3361 0 131935.8118 285 139646.7016 Cuts: 14 116866 5.52%\n", + " 3361 0 131938.1625 289 139646.7016 Cuts: 8 116891 5.52%\n", + " 3361 2 131938.1625 289 139646.7016 131938.1625 116891 5.52%\n", + " 3367 6 134076.0954 269 139646.7016 132042.0174 117275 5.45%\n", + " 3409 22 134341.7852 259 139646.7016 132085.8642 119162 5.41%\n", + " 3543 140 134509.2707 232 139646.7016 132085.8642 127328 5.41%\n", + " 4843 1278 134245.8223 228 139646.7016 132265.5197 183399 5.29%\n", + "Elapsed time = 8.94 sec. (5524.29 ticks, tree = 1.93 MB, solutions = 4)\n", + " 6266 2524 137547.4624 110 139646.7016 132521.6531 242943 5.10%\n", + " 7389 3435 134058.1834 213 139646.7016 132651.7138 297032 5.01%\n", + " 8368 4270 134643.6490 282 139646.7016 132728.2962 353493 4.95%\n", + " 9440 5145 135392.4955 206 139646.7016 132821.6806 409107 4.89%\n", + " 10591 6186 139611.6711 23 139646.7016 132871.6602 477619 4.85%\n", + "* 10972+ 6411 139597.2486 132891.7589 4.80%\n", + " 11511 6946 139372.7580 154 139597.2486 132920.8306 528712 4.78%\n", + " 12700 7927 136712.7928 163 139597.2486 132998.7590 600794 4.73%\n", + " 13526 8625 135044.3151 264 139597.2486 133021.9190 651371 4.71%\n", + " 14697 9679 138011.1315 73 139597.2486 133081.9134 730699 4.67%\n", + " 15899 10595 137817.1395 172 139597.2486 133112.9450 793658 4.65%\n", + "Elapsed time = 30.52 sec. (15073.95 ticks, tree = 14.27 MB, solutions = 5)\n", + " 17272 11630 134373.6638 268 139597.2486 133160.0538 863352 4.61%\n", + " 18154 12456 135512.2390 214 139597.2486 133184.3101 914294 4.59%\n", + "* 19103+12756 138557.9514 133201.2712 3.87%\n", + "* 19112+12756 138555.8592 133201.2712 3.86%\n", + "* 19119+12754 138176.0250 133201.2712 3.60%\n", + " 19568 11006 138114.2953 168 138176.0250 133211.7638 980893 3.59%\n", + "* 20151+11679 137931.3403 133230.2930 3.41%\n", + " 20724 11645 135520.1113 214 137931.3403 133254.4072 1040363 3.39%\n", + " 22071 12638 135865.6053 257 137931.3403 133297.7305 1091435 3.36%\n", + " 23514 13606 134814.4730 255 137931.3403 133345.3677 1146588 3.32%\n", + " 24918 14781 136680.0635 117 137931.3403 133376.6396 1201926 3.30%\n", + " 25819 15722 135212.1242 248 137931.3403 133411.7951 1254986 3.28%\n", + " 27285 16445 136487.2357 232 137931.3403 133439.3627 1287963 3.26%\n", + "* 27498+16900 137633.6869 133439.3627 3.05%\n", + "* 27620+16153 137614.8795 133444.0290 3.03%\n", + "* 27988 16298 integral 0 136791.2353 133455.4499 1327547 2.44%\n", + " 28550 16506 134061.2533 230 136791.2353 133465.0589 1340886 2.43%\n", + "Elapsed time = 52.05 sec. (24621.12 ticks, tree = 98.33 MB, solutions = 13)\n", + "* 28831+14021 136727.4906 133474.1316 2.38%\n", + " 29860 14469 cutoff 136727.4906 133504.4915 1408862 2.36%\n", + " 31097 15287 135423.8747 243 136727.4906 133529.5448 1460859 2.34%\n", + " 32315 15888 136320.9850 237 136727.4906 133572.6573 1495844 2.31%\n", + " 33325 16979 136576.9221 176 136727.4906 133600.6133 1560159 2.29%\n", + " 34392 17688 135452.0679 234 136727.4906 133630.6860 1611112 2.26%\n", + " 35450 18153 134955.9053 195 136727.4906 133652.7810 1642298 2.25%\n", + " 36061 18887 135235.3816 141 136727.4906 133673.6720 1686361 2.23%\n", + "\n", + "Performing restart 2\n", + "\n", + "Repeating presolve.\n", + "Tried aggregator 2 times.\n", + "MIP Presolve eliminated 117 rows and 34 columns.\n", + "MIP Presolve modified 9 coefficients.\n", + "Aggregator did 1 substitutions.\n", + "Reduced MIP has 2128 rows, 1109 columns, and 7674 nonzeros.\n", + "Reduced MIP has 554 binaries, 555 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.00 sec. (6.72 ticks)\n", + "Tried aggregator 1 time.\n", + "Reduced MIP has 2128 rows, 1109 columns, and 7674 nonzeros.\n", + "Reduced MIP has 554 binaries, 555 generals, 0 SOSs, and 0 indicators.\n", + "Presolve time = 0.00 sec. (6.11 ticks)\n", + "Represolve time = 0.16 sec. (43.29 ticks)\n", + " 36132 0 132293.7973 308 136727.4906 Cuts: 32 1720307 2.22%\n", + " 36132 0 132329.2126 305 136727.4906 Cuts: 13 1720363 2.22%\n", + " 36132 0 132359.9002 298 136727.4906 Cuts: 29 1720425 2.22%\n", + " 36132 0 132395.5486 288 136727.4906 Cuts: 32 1720486 2.22%\n", + " 36132 0 132436.0002 302 136727.4906 Cuts: 45 1720551 2.22%\n", + " 36132 0 132449.3387 309 136727.4906 Cuts: 40 1720585 2.22%\n", + " 36132 0 132455.1376 303 136727.4906 Cuts: 27 1720620 2.22%\n", + " 36132 0 132460.3898 307 136727.4906 Cuts: 29 1720643 2.22%\n", + " 36132 0 132466.8279 308 136727.4906 Cuts: 10 1720673 2.22%\n", + " 36132 0 132477.0978 308 136727.4906 Cuts: 31 1720733 2.22%\n", + " 36132 2 132477.0978 308 136727.4906 133686.6392 1720733 2.22%\n", + " 36186 33 133255.8389 273 136727.4906 133686.6392 1724269 2.22%\n", + " 36296 121 136256.7607 190 136727.4906 133686.6392 1732167 2.22%\n", + "Elapsed time = 74.42 sec. (35299.15 ticks, tree = 0.30 MB, solutions = 14)\n", + " 36439 210 134278.6612 257 136727.4906 133686.6392 1740517 2.22%\n", + " 36743 389 134415.5064 224 136727.4906 133686.6392 1755093 2.22%\n", + " 37455 829 134954.0088 108 136727.4906 133686.6392 1783996 2.22%\n", + " 38295 1383 133926.1412 263 136727.4906 133686.6392 1827656 2.22%\n", + " 39375 2415 136424.2118 161 136727.4906 133686.6392 1906014 2.22%\n", + " 40260 2946 135622.0388 249 136727.4906 133686.6392 1945566 2.22%\n", + "* 41332 3710 integral 0 135389.0402 133686.6392 1992435 1.26%\n", + " 41441 2417 134564.6842 190 135389.0402 133686.6392 2005355 1.26%\n", + "* 41604+ 2470 135144.3554 133686.6392 1.08%\n", + " 42552 2653 134328.1768 153 135144.3554 133686.6392 2060066 1.08%\n", + " 43741 3086 134616.2083 273 135144.3554 133814.6561 2109233 0.98%\n", + "* 44539+ 3553 134904.6780 133914.7780 0.73%\n", + " 44869 2821 134855.6741 243 134904.6780 133938.6600 2162631 0.72%\n", + "Elapsed time = 94.92 sec. (44845.50 ticks, tree = 19.52 MB, solutions = 17)\n", + " 45757 2978 134546.3985 166 134904.6780 134037.3501 2211164 0.64%\n", + " 46869 3005 cutoff 134904.6780 134193.9212 2263290 0.53%\n", + " 48006 2748 cutoff 134904.6780 134371.3730 2319854 0.40%\n", + " 49359 2022 cutoff 134904.6780 134577.2058 2371738 0.24%\n", + "\n", + "GUB cover cuts applied: 9\n", + "Cover cuts applied: 17\n", + "Flow cuts applied: 53\n", + "Mixed integer rounding cuts applied: 196\n", + "Zero-half cuts applied: 44\n", + "Lift and project cuts applied: 55\n", + "Gomory fractional cuts applied: 5\n", + "\n", + "Root node processing (before b&c):\n", + " Real time = 0.78 sec. (447.51 ticks)\n", + "Parallel b&c, 8 threads:\n", + " Real time = 103.36 sec. (48328.79 ticks)\n", + " Sync time (average) = 14.13 sec.\n", + " Wait time (average) = 0.05 sec.\n", + " ------------\n", + "Total (root+branch&cut) = 104.14 sec. (48776.30 ticks)\n" + ] + } + ], + "source": [ + "cost, state = wfn.design()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "8db2a408-c01a-407b-a68e-502b582a4aeb", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "wfn.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "f6037ded-527f-4e77-aa28-c58f7b355527", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "43229157 €\n" + ] + } + ], + "source": [ + "print(round(cost), ' €')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8152bfdc-acaf-422e-9a37-02b1ec3c5f58", + "metadata": {}, + "outputs": [], + "source": [ + "# End of the code!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Edwin_Jupyter", + "language": "python", + "name": "edwin_jupyter" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/IEA37_Borssele/IEA37_10MW_Turbine.yaml b/docs/notebooks/IEA37_Borssele/IEA37_10MW_Turbine.yaml new file mode 100644 index 0000000..4070ae1 --- /dev/null +++ b/docs/notebooks/IEA37_Borssele/IEA37_10MW_Turbine.yaml @@ -0,0 +1,13 @@ +name: IEA Wind Task 37 10MW Offshore Reference Turbine +performance: + rated_power: 10000000 + cutin_wind_speed: 4.0 + cutout_wind_speed: 25.0 + Cp_curve: + Cp_values: [0.3251078849824, 0.3761866281728, 0.4027145387030, 0.4155602463718, 0.4229576890723, 0.4274423707763, 0.4292571166454, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4297950236451, 0.4305352571826, 0.4312166284858, 0.4312814548898, 0.3514206079661, 0.3111872737234, 0.2749536435990, 0.2425788253149, 0.2138334167044, 0.1884425673193, 0.1661051864306, 0.1465147713619, 0.1293725899244, 0.1143962632917, 0.1013248093380, 0.0899210931142, 0.0799724493340, 0.0712900820529, 0.0637077082248, 0.0570797722614, 0.0512794666863, 0.0461967165383, 0.0417362286262, 0.0378156660463, 0.0343641222803] + Cp_wind_speeds: [4, 4.51465256241674, 5.00079568768590, 5.45735001649664, 5.88330188406568, 6.27770557072665, 6.63968540166425, 6.96843769113132, 7.26323252683214, 7.52341539051034, 7.74840861114305, 7.93771264751504, 8.09090719732515, 8.20765213036254, 8.28768824368072, 8.33083783709303, 8.33700510771140, 8.36783385277002, 8.43559016598207, 8.54012361138798, 8.68120209859766, 8.85851239808912, 9.07166083665646, 9.32017417146271, 9.60350064075739, 9.92101118892548, 10.27200086314830, 10.65569037857490, 10.75773634661880, 11.51769067591280, 11.99408760159840, 12.49936090527570, 13.03238875385400, 13.59198769221170, 14.17691527076350, 14.78587280400860, 15.41750825393750, 16.07041923189440, 16.74315611223090, 17.43422525083730, 18.14209230140590, 18.86518562206390, 19.60189976481120, 20.35059904001620, 21.10962114805620, 21.87728087003820, 22.65187380940600, 23.43168017612710, 24.21496860505640, 25] + Ct_curve: + Ct_values: [0.7701137761237, 0.7763017654756, 0.7824304039822, 0.7819829934196, 0.7802469277997, 0.7771530060613, 0.7718536866178, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7768459631292, 0.7675219106211, 0.7651043465733, 0.7586750258934, 0.5056495638933, 0.4310356122709, 0.3708275390149, 0.3208559287394, 0.2788055309144, 0.2431510192490, 0.2127735581146, 0.1868037845539, 0.1645422214162, 0.1454145646300, 0.1289436445432, 0.1147302834935, 0.1024389909637, 0.0917869641823, 0.0825353277738, 0.0744820092911, 0.0674558666278, 0.0613117663085, 0.0559264801561, 0.0511952613502, 0.0470291247090] + Ct_wind_speeds: [4, 4.51465256241674, 5.00079568768590, 5.45735001649664, 5.88330188406568, 6.27770557072665, 6.63968540166425, 6.96843769113132, 7.26323252683214, 7.52341539051034, 7.74840861114305, 7.93771264751504, 8.09090719732515, 8.20765213036254, 8.28768824368072, 8.33083783709303, 8.33700510771140, 8.36783385277002, 8.43559016598207, 8.54012361138798, 8.68120209859766, 8.85851239808912, 9.07166083665646, 9.32017417146271, 9.60350064075739, 9.92101118892548, 10.27200086314830, 10.65569037857490, 10.75773634661880, 11.51769067591280, 11.99408760159840, 12.49936090527570, 13.03238875385400, 13.59198769221170, 14.17691527076350, 14.78587280400860, 15.41750825393750, 16.07041923189440, 16.74315611223090, 17.43422525083730, 18.14209230140590, 18.86518562206390, 19.60189976481120, 20.35059904001620, 21.10962114805620, 21.87728087003820, 22.65187380940600, 23.43168017612710, 24.21496860505640, 25] +hub_height: 119.0 +rotor_diameter: 198.0 diff --git a/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Irregular.yaml b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Irregular.yaml new file mode 100644 index 0000000..555813c --- /dev/null +++ b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Irregular.yaml @@ -0,0 +1,53 @@ +name: IEA Wind Task 37 Borssele Reference Offshore Wind Plant (irregular layout) +layouts: + initial_layout: + coordinates: + x: [501266.54317574, 493922.24954271, 502907.19014959, 502095.12557912, + 496804.65625979, 485102.77027462, 490588.37581976, 502784.83623495, + 503032.60103984, 484578.18744373, 486707.11173729, 492087.72419315, + 502245.04987847, 486993.72883541, 486339.8895555 , 501937.23428743, + 490866.23972975, 489583.97960791, 493244.9730643 , 484184.85247367, + 495837.28295403, 501763.67866768, 497805.65064534, 502520.00847477, + 488196.97665934, 502261.66468538, 487660.64327449, 496326.9935416 , + 495560.72737215, 492442.84358768, 489811.54071237, 488384.83348118, + 485719.30135799, 499693.18157517, 494872.64094661, 491373.54080591, + 492179.32192035, 497223.78998917, 497331.96509787, 496443.58939824, + 499696.09772372, 497791.49735685, 503157.65666188, 502656.8554572 , + 499013.99779544, 502389.74235056, 499568.71665005, 498412.30882307, + 500126.55758186, 493699.48183891, 500599.36370223, 497855.63257599, + 495278.26879751, 493700.84819603, 492224.36209737, 498554.39258276, + 489085.25534532, 500588.51971806, 499488.70435074, 494285.31394068, + 495302.17515336, 498899.58556002, 493038.70804906, 494794.71182856, + 498335.26945098, 499450.50531967, 501551.75352904, 499153.2151173 , + 489443.95885503, 495667.21147171, 499425.06290419, 497613.73273252, + 501417.31149458, 490809.4125091 ] + y: [5715995.89368689, 5723126.80050036, 5727399.9805506 , + 5721783.86274859, 5720326.91432108, 5731616.65356541, + 5726358.98043443, 5726554.75877663, 5728282.55126408, + 5732113.07451875, 5733271.99003168, 5734978.77939308, + 5722819.59436817, 5729811.23565774, 5730437.7600403 , + 5720684.82136608, 5734593.97542309, 5734183.81996312, + 5735342.15989407, 5732482.14974234, 5721263.84408409, + 5719468.98024809, 5719362.35078842, 5724757.97736015, + 5733753.39470066, 5729567.10859905, 5729175.80279442, + 5736318.33484618, 5730365.5374425 , 5732709.48377899, + 5727115.20095509, 5728488.33293297, 5731024.64807625, + 5726888.85185297, 5722211.95206308, 5725600.69245181, + 5724815.96578192, 5723028.55637662, 5736641.50567781, + 5729036.22360224, 5730763.14987881, 5727794.82805826, + 5729153.34310612, 5725664.84960664, 5718720.22921093, + 5723814.40501551, 5717641.93281177, 5736984.27770828, + 5737528.57233063, 5728053.24974494, 5716646.9421783 , + 5725266.2923445 , 5735992.35112698, 5731256.8927141 , + 5729156.69983083, 5731292.34250883, 5727820.39671998, + 5730350.50472903, 5736258.41812358, 5735679.58364592, + 5733057.09366068, 5735074.06082225, 5723984.10312561, + 5726569.83357447, 5733940.1526999 , 5737313.57272465, + 5718018.11318477, 5721395.33171942, 5731966.4812102 , + 5725151.81249156, 5723990.57679841, 5732484.17798327, + 5729964.36557568, 5730586.99083753] +electrical_substations: + coordinates: + x: [497620.7] + y: [5730622.0] +turbines: !include IEA37_10MW_Turbine.yaml diff --git a/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Irregular_System.yaml b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Irregular_System.yaml new file mode 100644 index 0000000..a77b823 --- /dev/null +++ b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Irregular_System.yaml @@ -0,0 +1,9 @@ +name: IEA Wind Task 37 Borssele Reference Offshore Wind Plant system (irregular layout) +site: !include IEA37_Borssele_site.yaml +wind_farm: !include IEA37_Borssele_Irregular.yaml +attributes: + net_AEP: 3476.25 + analyses: + wake_model: + name: Jensen + diff --git a/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Regular.yaml b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Regular.yaml new file mode 100644 index 0000000..180ccd0 --- /dev/null +++ b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Regular.yaml @@ -0,0 +1,43 @@ +name: IEA Wind Task 37 Borssele Reference Offshore Wind Plant (regular layout) +layouts: + initial_layout: + coordinates: + x: [ + 500968.1461, 499748.6565, 501245.7744, 500026.2848, 498527.8286, + 497308.339, 501523.4027, 500303.9131, 498805.4569, 497585.9673, + 496087.5111, 494868.0215, 501801.031, 500581.5414, 499083.0852, + 497863.5956, 496365.1394, 495145.6498, 493647.1936, 492427.704, + 502078.6593, 500859.1697, 499360.7135, 498141.2239, 496642.7677, + 495423.2781, 493924.8219, 492705.3323, 491206.8762, 489987.3865, + 502356.2876, 501136.798, 499638.3418, 498418.8522, 496920.396, + 495700.9064, 494202.4502, 492982.9606, 491484.5045, 490265.0148, + 488766.5587, 487547.069, 502633.9159, 501414.4263, 499915.9701, + 498696.4805, 497198.0243, 495978.5347, 494480.0786, 493260.5889, + 491762.1328, 490542.6431, 489044.187, 487824.6973, 486326.2412, + 485106.7515, 497475.6526, 496256.163, 494757.7069, 493538.2172, + 492039.7611, 490820.2714, 489321.8153, 488102.3256, 486603.8695, + 497753.2809, 496533.7913, 495035.3352, 493815.8455, 492317.3894, + 489599.4436, 498030.9093, 496811.4196, 495312.9635 + ] + y: [ + 5716452.784, 5717635.848, 5718427.22, 5719610.283, 5718809.394, + 5719992.458, 5720401.656, 5721584.719, 5720783.83, 5721966.894, + 5721166.004, 5722349.068, 5722376.092, 5723559.155, 5722758.266, + 5723941.33, 5723140.44, 5724323.504, 5723522.615, 5724705.678, + 5724350.528, 5725533.591, 5724732.702, 5725915.765, 5725114.876, + 5726297.94, 5725497.051, 5726680.114, 5725879.225, 5727062.288, + 5726324.963, 5727508.027, 5726707.138, 5727890.201, 5727089.312, + 5728272.376, 5727471.486, 5728654.55, 5727853.661, 5729036.724, + 5728235.835, 5729418.899, 5728299.399, 5729482.463, 5728681.574, + 5729864.637, 5729063.748, 5730246.812, 5729445.922, 5730628.986, + 5729828.097, 5731011.16, 5730210.271, 5731393.335, 5730592.445, + 5731775.509, 5731038.184, 5732221.248, 5731420.358, 5732603.422, + 5731802.533, 5732985.596, 5732184.707, 5733367.77, 5732566.881, + 5733012.62, 5734195.683, 5733394.794, 5734577.858, 5733776.968, + 5734159.143, 5734987.056, 5736170.119, 5735369.23 + ] +electrical_substations: + coordinates: + x: [497620.7] + y: [5730622.0] +turbines: !include IEA37_10MW_Turbine.yaml diff --git a/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Regular_System.yaml b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Regular_System.yaml new file mode 100644 index 0000000..49b3061 --- /dev/null +++ b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Regular_System.yaml @@ -0,0 +1,9 @@ +name: IEA Wind Task 37 Borssele Reference Offshore Wind Plant system (regular layout) +site: !include IEA37_Borssele_site.yaml +wind_farm: !include IEA37_Borssele_Regular.yaml +attributes: + net_AEP: 3431.43 + analyses: + wake_model: + name: Jensen + diff --git a/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Site.yaml b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Site.yaml new file mode 100644 index 0000000..d765761 --- /dev/null +++ b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Site.yaml @@ -0,0 +1,10 @@ +name: IEA Wind Task 37 case study Borssele, 74WT Wind Farm +boundaries: + polygons: [ + { + x: [484178.55, 500129.9, 497318.1, 497318.1, 503163.37, 501266.5, 488951.0], + y: [5732482.8, 5737534.4, 5731880.24, 5731880.24, 5729155.3, 5715990.05, 5.72794e+06] + } + ] +energy_resource: !include IEA37_Borssele_Wind_Resource.yaml +# Bathymetry: !includeBathymetryNetCDF bathymetry.nc diff --git a/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Wind_Resource.yaml b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Wind_Resource.yaml new file mode 100644 index 0000000..18d3a02 --- /dev/null +++ b/docs/notebooks/IEA37_Borssele/IEA37_Borssele_Wind_Resource.yaml @@ -0,0 +1,115 @@ +name: Borssele Energy Resource +wind_resource: + sector_probability: + data: + - 0.06692 + - 0.07626 + - 0.07374 + - 0.06463 + - 0.04696 + - 0.04643 + - 0.07672 + - 0.12233 + - 0.19147 + - 0.10080 + - 0.06927 + - 0.06447 + dims: + - wind_direction + turbulence_intensity: + data: + - 0.144 + - 0.121 + - 0.106 + - 0.095 + - 0.087 + - 0.081 + - 0.076 + - 0.071 + - 0.068 + - 0.065 + - 0.063 + - 0.060 + - 0.058 + - 0.057 + - 0.055 + - 0.054 + - 0.053 + - 0.052 + - 0.051 + - 0.050 + - 0.049 + - 0.048 + dims: + - wind_speed + shear: + alpha: 0.08 + h_ref: 119 + weibull_a: + data: + - 9.08 + - 9.30 + - 9.18 + - 8.89 + - 8.13 + - 8.76 + - 11.38 + - 12.58 + - 12.74 + - 10.80 + - 9.76 + - 9.63 + dims: + - wind_direction + weibull_k: + data: + - 2.22 + - 2.26 + - 2.28 + - 2.28 + - 2.15 + - 2.11 + - 2.13 + - 2.29 + - 2.43 + - 2.09 + - 2.01 + - 2.01 + dims: + - wind_direction + wind_direction: + - 0.0 + - 30.0 + - 60.0 + - 90.0 + - 120.0 + - 150.0 + - 180.0 + - 210.0 + - 240.0 + - 270.0 + - 300.0 + - 330.0 + wind_speed: + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 + - 16 + - 17 + - 18 + - 19 + - 20 + - 21 + - 22 + - 23 + - 24 + - 25 diff --git a/docs/notebooks/IEA37_Borssele_Cable_Optimization.ipynb b/docs/notebooks/IEA37_Borssele_Cable_Optimization.ipynb new file mode 100644 index 0000000..20ac2cb --- /dev/null +++ b/docs/notebooks/IEA37_Borssele_Cable_Optimization.ipynb @@ -0,0 +1,2603 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1a1fae02-e4a3-4bf4-b339-b5d8fe5bb529", + "metadata": {}, + "source": [ + "# EDWIN cable layout optimization of IEA Wind Task 37 Borssele Reference Offshore Wind Plant (CBC solver)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f788f477-b4b4-405b-afd8-15dd5221ce68", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:25:33.064002Z", + "iopub.status.busy": "2024-02-06T13:25:33.063015Z", + "iopub.status.idle": "2024-02-06T13:25:38.723282Z", + "shell.execute_reply": "2024-02-06T13:25:38.723282Z", + "shell.execute_reply.started": "2024-02-06T13:25:33.064002Z" + } + }, + "outputs": [], + "source": [ + "from ed_win.wind_farm_network import WindFarmNetwork, InterArrayDriver\n", + "from windIO.utils.yml_utils import load_yaml\n", + "import numpy as np\n", + "import xarray as xr\n", + "import psutil\n", + "import os" + ] + }, + { + "cell_type": "markdown", + "id": "3dfc99b7-0943-4e76-b9bd-fbc4f1c4f72a", + "metadata": {}, + "source": [ + "## System information" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "5c69a05c-d986-4288-9a70-8978a7ff3ef8", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:25:38.725279Z", + "iopub.status.busy": "2024-02-06T13:25:38.725279Z", + "iopub.status.idle": "2024-02-06T13:25:38.738981Z", + "shell.execute_reply": "2024-02-06T13:25:38.738871Z", + "shell.execute_reply.started": "2024-02-06T13:25:38.725279Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total Memory: 31.67 GB\n", + "Available Memory: 12.10 GB\n", + "Available cores: 16\n", + "Logical cores/real cores: 2.0\n", + "Available physical cores: 8\n", + "CPU Frequency: 2496.0 MHz\n" + ] + } + ], + "source": [ + "# Get memory information\n", + "memory_info = psutil.virtual_memory()\n", + "\n", + "# Print available memory\n", + "print(f\"Total Memory: {memory_info.total / (1024 ** 3):.2f} GB\")\n", + "print(f\"Available Memory: {memory_info.available / (1024 ** 3):.2f} GB\")\n", + "\n", + "# Get available CPU cores\n", + "core_count = len(psutil.Process().cpu_affinity())\n", + "logical_to_physical_core_ratio = psutil.cpu_count(logical=True)/psutil.cpu_count(logical=False)\n", + "physical_cores = int(core_count/logical_to_physical_core_ratio)\n", + "\n", + "# Get CPU frequency\n", + "cpu_freq = psutil.cpu_freq()\n", + "\n", + "# Print the information\n", + "print(f\"Available cores: {core_count}\\n\"\n", + " f\"Logical cores/real cores: {logical_to_physical_core_ratio}\\n\"\n", + " f\"Available physical cores: {physical_cores}\")\n", + "print(f\"CPU Frequency: {cpu_freq.current} MHz\")" + ] + }, + { + "cell_type": "markdown", + "id": "1d794d44-fdd0-4a92-986f-4fad2df55e28", + "metadata": {}, + "source": [ + "## Optimization settings" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "adbae7b8-a026-41a5-bad6-bea48496e99d", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:25:38.746994Z", + "iopub.status.busy": "2024-02-06T13:25:38.746994Z", + "iopub.status.idle": "2024-02-06T13:25:38.754995Z", + "shell.execute_reply": "2024-02-06T13:25:38.754995Z", + "shell.execute_reply.started": "2024-02-06T13:25:38.746994Z" + } + }, + "outputs": [], + "source": [ + "interarray_setting = {\n", + " 'solver_name': 'cbc', # choose between cplex, cbc,\n", + " 'gap': 0.002, # relative gap to optimality bound\n", + " 'timelimit': 7200, # seconds\n", + " 'other_settings': {\n", + " 'relax_boundary': True,\n", + " 'branching': True,\n", + " 'gateXings_constraint': True,\n", + " 'gates_limit': False,\n", + " 'threads': physical_cores,\n", + " 'warmstart': True,\n", + " 'tee': True,\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "c94028aa-87e6-4d2e-a4d3-d7bce37dfd57", + "metadata": {}, + "source": [ + "## Cable data" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "bfec0b6b-c992-4edd-ad3a-476a1a6d74d9", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:25:38.757990Z", + "iopub.status.busy": "2024-02-06T13:25:38.757990Z", + "iopub.status.idle": "2024-02-06T13:25:38.771994Z", + "shell.execute_reply": "2024-02-06T13:25:38.770993Z", + "shell.execute_reply.started": "2024-02-06T13:25:38.757990Z" + } + }, + "outputs": [], + "source": [ + "# Cable data\n", + "cable_costs = [206, 287, 406] # [€/m] Costs per distance for each cable type\n", + "turbines_per_cable = [3, 5, 7]\n", + "cross_section = [500, 1000, 1500] # [mm²]\n", + "cables = np.array([[section, capacity, cost]\n", + " for section, capacity, cost in\n", + " zip(cross_section, turbines_per_cable, cable_costs)])" + ] + }, + { + "cell_type": "markdown", + "id": "33c032b8-f384-4551-b477-5eca6dc2fc6c", + "metadata": {}, + "source": [ + "## Regular layout" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "82dde137-eb8c-48a1-87fd-ca9f3be93249", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:26:22.408364Z", + "iopub.status.busy": "2024-02-06T13:26:22.407364Z", + "iopub.status.idle": "2024-02-06T13:26:22.496651Z", + "shell.execute_reply": "2024-02-06T13:26:22.495654Z", + "shell.execute_reply.started": "2024-02-06T13:26:22.408364Z" + }, + "scrolled": true + }, + "outputs": [], + "source": [ + "# Load Regular layout from YAML file\n", + "regular_system = load_yaml('IEA37_Borssele/IEA37_Borssele_Regular_System.yaml')\n", + "\n", + "turbine_pos = np.array([\n", + " regular_system['wind_farm']['layouts']['initial_layout']['coordinates']['x'],\n", + " regular_system['wind_farm']['layouts']['initial_layout']['coordinates']['y']\n", + "])\n", + "substation_pos = np.array([\n", + " regular_system['wind_farm']['electrical_substations']['coordinates']['x'],\n", + " regular_system['wind_farm']['electrical_substations']['coordinates']['y']\n", + "])\n", + "\n", + "main_parcel = np.array([regular_system['site']['boundaries']['polygons'][0]['x'],\n", + " regular_system['site']['boundaries']['polygons'][0]['y']])\n", + "\n", + "site_info = {'site_name': regular_system['name'],\n", + " 'handle': 'iea37reg',\n", + " 'boundary': main_parcel.T}" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "1f394116-1fbd-40fb-920a-15f3ae53fcec", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:26:23.417690Z", + "iopub.status.busy": "2024-02-06T13:26:23.417690Z", + "iopub.status.idle": "2024-02-06T13:26:23.424810Z", + "shell.execute_reply": "2024-02-06T13:26:23.424690Z", + "shell.execute_reply.started": "2024-02-06T13:26:23.417690Z" + } + }, + "outputs": [], + "source": [ + "wfn = WindFarmNetwork(turbine_positions=turbine_pos,\n", + " substation_positions=substation_pos,\n", + " drivers=[InterArrayDriver(**interarray_setting)],\n", + " sequence=[0],\n", + " cables=cables,\n", + " site_info=site_info)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "77ae09e9-e836-4101-95f3-6c40daea26fb", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:26:25.135196Z", + "iopub.status.busy": "2024-02-06T13:26:25.135196Z", + "iopub.status.idle": "2024-02-06T13:29:57.897000Z", + "shell.execute_reply": "2024-02-06T13:29:57.895991Z", + "shell.execute_reply.started": "2024-02-06T13:26:25.135196Z" + }, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Solving \"IEA Wind Task 37 Borssele Reference Offshore Wind Plant system (regular layout)\"\n", + "\n", + "Welcome to the CBC MILP Solver \n", + "Version: 2.10.8 \n", + "Build Date: Jan 1 1970 \n", + "\n", + "command line - C:\\Users\\s213184\\programs\\cbc_julia_mingw32_2.10.8\\bin\\cbc.exe -ratioGap 0.002 -seconds 7200 -timeMode elapsed -threads 8 -printingOptions all -import C:\\Users\\s213184\\AppData\\Local\\Temp\\tmpv6r08dfx.pyomo.lp -mipstart \\Users\\s213184\\AppData\\Local\\Temp\\tmphbded7hh.cbc.soln -stat=1 -solve -solu C:\\Users\\s213184\\AppData\\Local\\Temp\\tmpv6r08dfx.pyomo.soln (default strategy 1)\n", + "ratioGap was changed from 0 to 0.002\n", + "seconds was changed from 1e+100 to 7200\n", + "Option for timeMode changed from cpu to elapsed\n", + "threads was changed from 0 to 8\n", + "Option for printingOptions changed from normal to all\n", + "opening mipstart file \\Users\\s213184\\AppData\\Local\\Temp\\tmphbded7hh.cbc.soln.\n", + "MIPStart values read for 148 variables.\n", + "Presolve 3599 (-76) rows, 1527 (-1) columns and 11742 (-836) elements\n", + "Statistics for presolved model\n", + "Original problem has 1528 integers (764 of which binary)\n", + "Presolved problem has 1527 integers (763 of which binary)\n", + "==== 764 zero objective 185 different\n", + "==== absolute objective values 185 different\n", + "==== for integers 764 zero objective 185 different\n", + "==== for integers absolute objective values 185 different\n", + "===== end objective counts\n", + "\n", + "\n", + "Problem has 3599 rows, 1527 columns (763 with objective) and 11742 elements\n", + "Column breakdown:\n", + "0 of type 0.0->inf, 764 of type 0.0->up, 0 of type lo->inf, \n", + "0 of type lo->up, 0 of type free, 0 of type fixed, \n", + "0 of type -inf->0.0, 0 of type -inf->up, 763 of type 0.0->1.0 \n", + "Row breakdown:\n", + "0 of type E 0.0, 147 of type E 1.0, 0 of type E -1.0, \n", + "1 of type E other, 0 of type G 0.0, 0 of type G 1.0, \n", + "1 of type G other, 1527 of type L 0.0, 1847 of type L 1.0, \n", + "76 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other, \n", + "0 of type Free \n", + "Continuous objective value is 134213 - 0.02 seconds\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 1438 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 1168 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 583 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 202 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 33 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 15 strengthened rows, 0 substitutions\n", + "Cgl0004I processed model has 3117 rows, 1528 columns (1528 integer (764 of which binary)) and 13396 elements\n", + "Cbc0045I MIPStart provided solution with cost 152084\n", + "Cbc0012I Integer solution of 152083.98 found by Reduced search after 0 iterations and 0 nodes (0.25 seconds)\n", + "Cbc0038I Full problem 3117 rows 1528 columns, reduced to 307 rows 164 columns\n", + "Cbc0031I 70 added rows had average density of 114.9\n", + "Cbc0013I At root node, 70 cuts changed objective from 135593.25 to 136487.34 in 11 passes\n", + "Cbc0014I Cut generator 0 (Probing) - 3 row cuts average 6.7 elements, 0 column cuts (0 active) in 0.075 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 1 (Gomory) - 188 row cuts average 412.5 elements, 0 column cuts (0 active) in 0.155 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 2 (Knapsack) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.021 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.004 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.028 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 5 (FlowCover) - 112 row cuts average 13.2 elements, 0 column cuts (0 active) in 0.014 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 6 (TwoMirCuts) - 252 row cuts average 216.8 elements, 0 column cuts (0 active) in 0.084 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 7 (ZeroHalf) - 13 row cuts average 12.2 elements, 0 column cuts (0 active) in 0.103 seconds - new frequency is -100\n", + "Cbc0010I After 0 nodes, 1 on tree, 152083.98 best solution, best possible 136487.34 (1.12 seconds)\n", + "Cbc0010I After 100 nodes, 59 on tree, 152083.98 best solution, best possible 136640.71 (3.03 seconds)\n", + "Cbc0010I After 200 nodes, 116 on tree, 152083.98 best solution, best possible 136640.71 (3.93 seconds)\n", + "Cbc0010I After 300 nodes, 168 on tree, 152083.98 best solution, best possible 136640.71 (4.53 seconds)\n", + "Cbc0010I After 400 nodes, 216 on tree, 152083.98 best solution, best possible 136640.71 (4.93 seconds)\n", + "Cbc0010I After 500 nodes, 269 on tree, 152083.98 best solution, best possible 136640.71 (5.22 seconds)\n", + "Cbc0010I After 600 nodes, 315 on tree, 152083.98 best solution, best possible 136640.71 (5.47 seconds)\n", + "Cbc0010I After 700 nodes, 350 on tree, 152083.98 best solution, best possible 136640.71 (5.61 seconds)\n", + "Cbc0012I Integer solution of 151998.92 found by heuristic after 51985 iterations and 730 nodes (5.66 seconds)\n", + "Cbc0012I Integer solution of 151698.71 found by heuristic after 52192 iterations and 734 nodes (5.67 seconds)\n", + "Cbc0010I After 800 nodes, 384 on tree, 151698.71 best solution, best possible 136640.71 (5.98 seconds)\n", + "Cbc0010I After 900 nodes, 431 on tree, 151698.71 best solution, best possible 136640.71 (6.34 seconds)\n", + "Cbc0010I After 1000 nodes, 481 on tree, 151698.71 best solution, best possible 136640.71 (6.66 seconds)\n", + "Cbc0010I After 1100 nodes, 531 on tree, 151698.71 best solution, best possible 136640.71 (7.00 seconds)\n", + "Cbc0010I After 1200 nodes, 577 on tree, 151698.71 best solution, best possible 136640.71 (7.29 seconds)\n", + "Cbc0010I After 1300 nodes, 627 on tree, 151698.71 best solution, best possible 136640.71 (7.60 seconds)\n", + "Cbc0010I After 1400 nodes, 671 on tree, 151698.71 best solution, best possible 136640.71 (7.81 seconds)\n", + "Cbc0010I After 1500 nodes, 715 on tree, 151698.71 best solution, best possible 136640.71 (8.01 seconds)\n", + "Cbc0010I After 1600 nodes, 766 on tree, 151698.71 best solution, best possible 136640.71 (8.20 seconds)\n", + "Cbc0010I After 1700 nodes, 803 on tree, 151698.71 best solution, best possible 136640.71 (8.35 seconds)\n", + "Cbc0010I After 1800 nodes, 847 on tree, 151698.71 best solution, best possible 136640.71 (8.46 seconds)\n", + "Cbc0010I After 1900 nodes, 889 on tree, 151698.71 best solution, best possible 136640.71 (8.59 seconds)\n", + "Cbc0010I After 2000 nodes, 930 on tree, 151698.71 best solution, best possible 136640.71 (8.71 seconds)\n", + "Cbc0010I After 2100 nodes, 969 on tree, 151698.71 best solution, best possible 136640.71 (8.84 seconds)\n", + "Cbc0010I After 2200 nodes, 1017 on tree, 151698.71 best solution, best possible 136640.71 (8.95 seconds)\n", + "Cbc0010I After 2300 nodes, 1055 on tree, 151698.71 best solution, best possible 136640.71 (9.10 seconds)\n", + "Cbc0010I After 2400 nodes, 1099 on tree, 151698.71 best solution, best possible 136640.71 (9.24 seconds)\n", + "Cbc0010I After 2500 nodes, 1148 on tree, 151698.71 best solution, best possible 136640.71 (9.33 seconds)\n", + "Cbc0010I After 2600 nodes, 1190 on tree, 151698.71 best solution, best possible 136640.71 (9.42 seconds)\n", + "Cbc0010I After 2700 nodes, 1230 on tree, 151698.71 best solution, best possible 136640.71 (9.52 seconds)\n", + "Cbc0012I Integer solution of 145706.07 found by heuristic after 161802 iterations and 2766 nodes (9.61 seconds)\n", + "Cbc0010I After 2800 nodes, 841 on tree, 145706.07 best solution, best possible 136640.71 (9.71 seconds)\n", + "Cbc0010I After 2900 nodes, 895 on tree, 145706.07 best solution, best possible 136640.71 (9.94 seconds)\n", + "Cbc0010I After 3000 nodes, 945 on tree, 145706.07 best solution, best possible 136640.71 (10.11 seconds)\n", + "Cbc0010I After 3100 nodes, 996 on tree, 145706.07 best solution, best possible 136640.71 (10.28 seconds)\n", + "Cbc0010I After 3200 nodes, 1044 on tree, 145706.07 best solution, best possible 136640.71 (10.46 seconds)\n", + "Cbc0010I After 3300 nodes, 1094 on tree, 145706.07 best solution, best possible 136640.71 (10.61 seconds)\n", + "Cbc0010I After 3400 nodes, 1144 on tree, 145706.07 best solution, best possible 136640.71 (10.75 seconds)\n", + "Cbc0012I Integer solution of 140719.49 found by heuristic after 198484 iterations and 3448 nodes (10.82 seconds)\n", + "Cbc0010I After 3500 nodes, 435 on tree, 140719.49 best solution, best possible 136640.71 (11.01 seconds)\n", + "Cbc0010I After 3600 nodes, 487 on tree, 140719.49 best solution, best possible 136640.71 (11.26 seconds)\n", + "Cbc0010I After 3700 nodes, 540 on tree, 140719.49 best solution, best possible 136640.71 (11.46 seconds)\n", + "Cbc0010I After 3800 nodes, 585 on tree, 140719.49 best solution, best possible 136640.71 (11.69 seconds)\n", + "Cbc0010I After 3900 nodes, 635 on tree, 140719.49 best solution, best possible 136640.71 (11.93 seconds)\n", + "Cbc0010I After 4000 nodes, 683 on tree, 140719.49 best solution, best possible 136640.71 (12.13 seconds)\n", + "Cbc0010I After 4100 nodes, 725 on tree, 140719.49 best solution, best possible 136640.71 (12.32 seconds)\n", + "Cbc0010I After 4200 nodes, 767 on tree, 140719.49 best solution, best possible 136640.71 (12.50 seconds)\n", + "Cbc0010I After 4300 nodes, 816 on tree, 140719.49 best solution, best possible 136640.71 (12.77 seconds)\n", + "Cbc0010I After 4400 nodes, 867 on tree, 140719.49 best solution, best possible 136640.71 (13.06 seconds)\n", + "Cbc0010I After 4500 nodes, 914 on tree, 140719.49 best solution, best possible 136640.71 (13.30 seconds)\n", + "Cbc0010I After 4600 nodes, 960 on tree, 140719.49 best solution, best possible 136640.71 (13.56 seconds)\n", + "Cbc0010I After 4700 nodes, 1004 on tree, 140719.49 best solution, best possible 136640.71 (13.80 seconds)\n", + "Cbc0010I After 4800 nodes, 1056 on tree, 140719.49 best solution, best possible 136640.71 (14.03 seconds)\n", + "Cbc0010I After 4900 nodes, 1106 on tree, 140719.49 best solution, best possible 136640.71 (14.17 seconds)\n", + "Cbc0010I After 5000 nodes, 1155 on tree, 140719.49 best solution, best possible 136640.71 (14.35 seconds)\n", + "Cbc0010I After 5100 nodes, 1191 on tree, 140719.49 best solution, best possible 136640.71 (14.49 seconds)\n", + "Cbc0010I After 5200 nodes, 1238 on tree, 140719.49 best solution, best possible 136640.71 (14.66 seconds)\n", + "Cbc0010I After 5300 nodes, 1280 on tree, 140719.49 best solution, best possible 136640.71 (14.84 seconds)\n", + "Cbc0010I After 5400 nodes, 1322 on tree, 140719.49 best solution, best possible 136640.71 (15.01 seconds)\n", + "Cbc0010I After 5500 nodes, 1363 on tree, 140719.49 best solution, best possible 136640.71 (15.20 seconds)\n", + "Cbc0010I After 5600 nodes, 1404 on tree, 140719.49 best solution, best possible 136640.71 (15.33 seconds)\n", + "Cbc0012I Integer solution of 139823.79 found by heuristic after 366173 iterations and 5691 nodes (15.50 seconds)\n", + "Cbc0010I After 5700 nodes, 1091 on tree, 139823.79 best solution, best possible 136640.71 (15.53 seconds)\n", + "Cbc0010I After 5800 nodes, 1143 on tree, 139823.79 best solution, best possible 136640.71 (15.76 seconds)\n", + "Cbc0010I After 5900 nodes, 1175 on tree, 139823.79 best solution, best possible 136640.71 (15.95 seconds)\n", + "Cbc0010I After 6000 nodes, 1218 on tree, 139823.79 best solution, best possible 136640.71 (16.16 seconds)\n", + "Cbc0010I After 6100 nodes, 1263 on tree, 139823.79 best solution, best possible 136640.71 (16.37 seconds)\n", + "Cbc0010I After 6200 nodes, 1311 on tree, 139823.79 best solution, best possible 136640.71 (16.57 seconds)\n", + "Cbc0010I After 6300 nodes, 1357 on tree, 139823.79 best solution, best possible 136640.71 (16.81 seconds)\n", + "Cbc0010I After 6400 nodes, 1400 on tree, 139823.79 best solution, best possible 136640.71 (17.01 seconds)\n", + "Cbc0010I After 6500 nodes, 1444 on tree, 139823.79 best solution, best possible 136640.71 (17.20 seconds)\n", + "Cbc0010I After 6600 nodes, 1485 on tree, 139823.79 best solution, best possible 136640.71 (17.38 seconds)\n", + "Cbc0010I After 6700 nodes, 1529 on tree, 139823.79 best solution, best possible 136640.71 (17.60 seconds)\n", + "Cbc0010I After 6800 nodes, 1559 on tree, 139823.79 best solution, best possible 136640.71 (17.81 seconds)\n", + "Cbc0010I After 6900 nodes, 1596 on tree, 139823.79 best solution, best possible 136640.71 (17.99 seconds)\n", + "Cbc0010I After 7000 nodes, 1634 on tree, 139823.79 best solution, best possible 136640.71 (18.12 seconds)\n", + "Cbc0010I After 7100 nodes, 1661 on tree, 139823.79 best solution, best possible 136640.71 (18.33 seconds)\n", + "Cbc0010I After 7200 nodes, 1683 on tree, 139823.79 best solution, best possible 136640.71 (18.53 seconds)\n", + "Cbc0010I After 7300 nodes, 1722 on tree, 139823.79 best solution, best possible 136640.71 (18.71 seconds)\n", + "Cbc0010I After 7400 nodes, 1765 on tree, 139823.79 best solution, best possible 136640.71 (18.94 seconds)\n", + "Cbc0010I After 7500 nodes, 1818 on tree, 139823.79 best solution, best possible 136640.71 (19.19 seconds)\n", + "Cbc0010I After 7600 nodes, 1860 on tree, 139823.79 best solution, best possible 136640.71 (19.40 seconds)\n", + "Cbc0010I After 7700 nodes, 1899 on tree, 139823.79 best solution, best possible 136640.71 (19.61 seconds)\n", + "Cbc0010I After 7800 nodes, 1945 on tree, 139823.79 best solution, best possible 136640.71 (19.85 seconds)\n", + "Cbc0010I After 7900 nodes, 1994 on tree, 139823.79 best solution, best possible 136640.71 (20.08 seconds)\n", + "Cbc0010I After 8000 nodes, 2039 on tree, 139823.79 best solution, best possible 136640.71 (20.31 seconds)\n", + "Cbc0010I After 8100 nodes, 2080 on tree, 139823.79 best solution, best possible 136640.71 (20.51 seconds)\n", + "Cbc0010I After 8200 nodes, 2122 on tree, 139823.79 best solution, best possible 136640.71 (20.71 seconds)\n", + "Cbc0010I After 8300 nodes, 2161 on tree, 139823.79 best solution, best possible 136640.71 (20.88 seconds)\n", + "Cbc0010I After 8400 nodes, 2194 on tree, 139823.79 best solution, best possible 136640.71 (21.07 seconds)\n", + "Cbc0010I After 8500 nodes, 2250 on tree, 139823.79 best solution, best possible 136640.71 (21.30 seconds)\n", + "Cbc0010I After 8600 nodes, 2295 on tree, 139823.79 best solution, best possible 136640.71 (21.48 seconds)\n", + "Cbc0010I After 8700 nodes, 2329 on tree, 139823.79 best solution, best possible 136640.71 (21.66 seconds)\n", + "Cbc0010I After 8800 nodes, 2368 on tree, 139823.79 best solution, best possible 136640.71 (21.86 seconds)\n", + "Cbc0010I After 8900 nodes, 2417 on tree, 139823.79 best solution, best possible 136640.71 (22.07 seconds)\n", + "Cbc0010I After 9000 nodes, 2462 on tree, 139823.79 best solution, best possible 136640.71 (22.24 seconds)\n", + "Cbc0010I After 9100 nodes, 2491 on tree, 139823.79 best solution, best possible 136640.71 (22.42 seconds)\n", + "Cbc0010I After 9200 nodes, 2522 on tree, 139823.79 best solution, best possible 136640.71 (22.59 seconds)\n", + "Cbc0010I After 9300 nodes, 2555 on tree, 139823.79 best solution, best possible 136640.71 (22.75 seconds)\n", + "Cbc0010I After 9400 nodes, 2589 on tree, 139823.79 best solution, best possible 136640.71 (22.93 seconds)\n", + "Cbc0010I After 9500 nodes, 2622 on tree, 139823.79 best solution, best possible 136640.71 (23.10 seconds)\n", + "Cbc0010I After 9600 nodes, 2650 on tree, 139823.79 best solution, best possible 136640.71 (23.29 seconds)\n", + "Cbc0010I After 9700 nodes, 2689 on tree, 139823.79 best solution, best possible 136640.71 (23.47 seconds)\n", + "Cbc0010I After 9800 nodes, 2716 on tree, 139823.79 best solution, best possible 136640.71 (23.60 seconds)\n", + "Cbc0010I After 9900 nodes, 2746 on tree, 139823.79 best solution, best possible 136640.71 (23.73 seconds)\n", + "Cbc0010I After 10000 nodes, 2769 on tree, 139823.79 best solution, best possible 136640.71 (23.87 seconds)\n", + "Cbc0010I After 10100 nodes, 2803 on tree, 139823.79 best solution, best possible 136640.71 (23.98 seconds)\n", + "Cbc0010I After 10200 nodes, 2829 on tree, 139823.79 best solution, best possible 136640.71 (24.11 seconds)\n", + "Cbc0010I After 10300 nodes, 2859 on tree, 139823.79 best solution, best possible 136640.71 (24.26 seconds)\n", + "Cbc0010I After 10400 nodes, 2894 on tree, 139823.79 best solution, best possible 136640.71 (24.41 seconds)\n", + "Cbc0010I After 10500 nodes, 2920 on tree, 139823.79 best solution, best possible 136640.71 (24.56 seconds)\n", + "Cbc0010I After 10600 nodes, 2950 on tree, 139823.79 best solution, best possible 136640.71 (24.77 seconds)\n", + "Cbc0010I After 10700 nodes, 2987 on tree, 139823.79 best solution, best possible 136640.71 (24.91 seconds)\n", + "Cbc0010I After 10800 nodes, 3015 on tree, 139823.79 best solution, best possible 136640.71 (25.07 seconds)\n", + "Cbc0010I After 10900 nodes, 3047 on tree, 139823.79 best solution, best possible 136640.71 (25.22 seconds)\n", + "Cbc0010I After 11000 nodes, 3089 on tree, 139823.79 best solution, best possible 136640.71 (25.37 seconds)\n", + "Cbc0010I After 11100 nodes, 3132 on tree, 139823.79 best solution, best possible 136766.14 (26.14 seconds)\n", + "Cbc0010I After 11200 nodes, 3182 on tree, 139823.79 best solution, best possible 136943.67 (26.64 seconds)\n", + "Cbc0010I After 11300 nodes, 3232 on tree, 139823.79 best solution, best possible 136960.29 (27.01 seconds)\n", + "Cbc0010I After 11400 nodes, 3282 on tree, 139823.79 best solution, best possible 137015.67 (27.34 seconds)\n", + "Cbc0010I After 11500 nodes, 3333 on tree, 139823.79 best solution, best possible 137045.56 (27.76 seconds)\n", + "Cbc0010I After 11600 nodes, 3381 on tree, 139823.79 best solution, best possible 137077.57 (28.03 seconds)\n", + "Cbc0010I After 11700 nodes, 3430 on tree, 139823.79 best solution, best possible 137096.74 (28.47 seconds)\n", + "Cbc0010I After 11800 nodes, 3479 on tree, 139823.79 best solution, best possible 137116.01 (28.82 seconds)\n", + "Cbc0010I After 11900 nodes, 3530 on tree, 139823.79 best solution, best possible 137133.44 (29.15 seconds)\n", + "Cbc0010I After 12000 nodes, 3580 on tree, 139823.79 best solution, best possible 137144.34 (29.47 seconds)\n", + "Cbc0010I After 12100 nodes, 3629 on tree, 139823.79 best solution, best possible 137160.31 (29.79 seconds)\n", + "Cbc0010I After 12200 nodes, 3678 on tree, 139823.79 best solution, best possible 137171.44 (30.03 seconds)\n", + "Cbc0010I After 12300 nodes, 3729 on tree, 139823.79 best solution, best possible 137184.69 (30.35 seconds)\n", + "Cbc0010I After 12400 nodes, 3779 on tree, 139823.79 best solution, best possible 137193.75 (30.65 seconds)\n", + "Cbc0010I After 12500 nodes, 3829 on tree, 139823.79 best solution, best possible 137205.85 (30.93 seconds)\n", + "Cbc0010I After 12600 nodes, 3878 on tree, 139823.79 best solution, best possible 137215.86 (31.24 seconds)\n", + "Cbc0010I After 12700 nodes, 3929 on tree, 139823.79 best solution, best possible 137229.82 (31.65 seconds)\n", + "Cbc0010I After 12800 nodes, 3980 on tree, 139823.79 best solution, best possible 137234.9 (31.98 seconds)\n", + "Cbc0010I After 12900 nodes, 4029 on tree, 139823.79 best solution, best possible 137246.28 (32.29 seconds)\n", + "Cbc0010I After 13000 nodes, 4077 on tree, 139823.79 best solution, best possible 137255.51 (32.56 seconds)\n", + "Cbc0010I After 13100 nodes, 4131 on tree, 139823.79 best solution, best possible 137256.12 (32.81 seconds)\n", + "Cbc0010I After 13200 nodes, 4179 on tree, 139823.79 best solution, best possible 137256.12 (33.06 seconds)\n", + "Cbc0010I After 13300 nodes, 4228 on tree, 139823.79 best solution, best possible 137256.12 (33.31 seconds)\n", + "Cbc0010I After 13400 nodes, 4277 on tree, 139823.79 best solution, best possible 137256.12 (33.53 seconds)\n", + "Cbc0010I After 13500 nodes, 4325 on tree, 139823.79 best solution, best possible 137256.12 (33.76 seconds)\n", + "Cbc0010I After 13600 nodes, 4371 on tree, 139823.79 best solution, best possible 137256.12 (33.99 seconds)\n", + "Cbc0010I After 13700 nodes, 4423 on tree, 139823.79 best solution, best possible 137256.12 (34.19 seconds)\n", + "Cbc0010I After 13800 nodes, 4468 on tree, 139823.79 best solution, best possible 137256.12 (34.43 seconds)\n", + "Cbc0010I After 13900 nodes, 4517 on tree, 139823.79 best solution, best possible 137256.12 (34.69 seconds)\n", + "Cbc0010I After 14000 nodes, 4566 on tree, 139823.79 best solution, best possible 137256.12 (34.88 seconds)\n", + "Cbc0010I After 14100 nodes, 4615 on tree, 139823.79 best solution, best possible 137266.65 (35.21 seconds)\n", + "Cbc0010I After 14200 nodes, 4663 on tree, 139823.79 best solution, best possible 137277.82 (35.52 seconds)\n", + "Cbc0010I After 14300 nodes, 4714 on tree, 139823.79 best solution, best possible 137289.88 (35.86 seconds)\n", + "Cbc0010I After 14400 nodes, 4764 on tree, 139823.79 best solution, best possible 137297.94 (36.18 seconds)\n", + "Cbc0010I After 14500 nodes, 4814 on tree, 139823.79 best solution, best possible 137306.3 (36.48 seconds)\n", + "Cbc0010I After 14600 nodes, 4864 on tree, 139823.79 best solution, best possible 137307.76 (36.75 seconds)\n", + "Cbc0010I After 14700 nodes, 4914 on tree, 139823.79 best solution, best possible 137323.96 (37.12 seconds)\n", + "Cbc0010I After 14800 nodes, 4965 on tree, 139823.79 best solution, best possible 137328.91 (37.39 seconds)\n", + "Cbc0010I After 14900 nodes, 5013 on tree, 139823.79 best solution, best possible 137338.94 (37.69 seconds)\n", + "Cbc0010I After 15000 nodes, 5063 on tree, 139823.79 best solution, best possible 137347.64 (37.97 seconds)\n", + "Cbc0010I After 15100 nodes, 5112 on tree, 139823.79 best solution, best possible 137353.64 (38.26 seconds)\n", + "Cbc0010I After 15200 nodes, 5160 on tree, 139823.79 best solution, best possible 137360.42 (38.49 seconds)\n", + "Cbc0010I After 15300 nodes, 5210 on tree, 139823.79 best solution, best possible 137365.51 (38.78 seconds)\n", + "Cbc0010I After 15400 nodes, 5260 on tree, 139823.79 best solution, best possible 137368.31 (39.03 seconds)\n", + "Cbc0010I After 15500 nodes, 5308 on tree, 139823.79 best solution, best possible 137375.85 (39.26 seconds)\n", + "Cbc0010I After 15600 nodes, 5357 on tree, 139823.79 best solution, best possible 137381.04 (39.51 seconds)\n", + "Cbc0010I After 15700 nodes, 5407 on tree, 139823.79 best solution, best possible 137387.48 (39.75 seconds)\n", + "Cbc0010I After 15800 nodes, 5457 on tree, 139823.79 best solution, best possible 137392.53 (39.97 seconds)\n", + "Cbc0010I After 15900 nodes, 5505 on tree, 139823.79 best solution, best possible 137397.54 (40.21 seconds)\n", + "Cbc0010I After 16000 nodes, 5553 on tree, 139823.79 best solution, best possible 137402.09 (40.44 seconds)\n", + "Cbc0010I After 16100 nodes, 5603 on tree, 139823.79 best solution, best possible 137406.17 (40.69 seconds)\n", + "Cbc0010I After 16200 nodes, 5652 on tree, 139823.79 best solution, best possible 137409.96 (40.92 seconds)\n", + "Cbc0010I After 16300 nodes, 5702 on tree, 139823.79 best solution, best possible 137415.05 (41.18 seconds)\n", + "Cbc0010I After 16400 nodes, 5752 on tree, 139823.79 best solution, best possible 137419.84 (41.43 seconds)\n", + "Cbc0010I After 16500 nodes, 5802 on tree, 139823.79 best solution, best possible 137424.88 (41.72 seconds)\n", + "Cbc0010I After 16600 nodes, 5853 on tree, 139823.79 best solution, best possible 137429.73 (41.99 seconds)\n", + "Cbc0010I After 16700 nodes, 5901 on tree, 139823.79 best solution, best possible 137434.26 (42.18 seconds)\n", + "Cbc0010I After 16800 nodes, 5952 on tree, 139823.79 best solution, best possible 137438.3 (42.46 seconds)\n", + "Cbc0010I After 16900 nodes, 5999 on tree, 139823.79 best solution, best possible 137441.92 (42.68 seconds)\n", + "Cbc0010I After 17000 nodes, 6048 on tree, 139823.79 best solution, best possible 137446.69 (42.92 seconds)\n", + "Cbc0010I After 17100 nodes, 6101 on tree, 139823.79 best solution, best possible 137447.33 (43.14 seconds)\n", + "Cbc0010I After 17200 nodes, 6150 on tree, 139823.79 best solution, best possible 137447.33 (43.35 seconds)\n", + "Cbc0010I After 17300 nodes, 6193 on tree, 139823.79 best solution, best possible 137447.33 (43.51 seconds)\n", + "Cbc0010I After 17400 nodes, 6238 on tree, 139823.79 best solution, best possible 137447.33 (43.74 seconds)\n", + "Cbc0010I After 17500 nodes, 6277 on tree, 139823.79 best solution, best possible 137447.33 (43.94 seconds)\n", + "Cbc0010I After 17600 nodes, 6321 on tree, 139823.79 best solution, best possible 137447.33 (44.13 seconds)\n", + "Cbc0010I After 17700 nodes, 6363 on tree, 139823.79 best solution, best possible 137447.33 (44.32 seconds)\n", + "Cbc0010I After 17800 nodes, 6402 on tree, 139823.79 best solution, best possible 137447.33 (44.51 seconds)\n", + "Cbc0010I After 17900 nodes, 6443 on tree, 139823.79 best solution, best possible 137447.33 (44.69 seconds)\n", + "Cbc0010I After 18000 nodes, 6477 on tree, 139823.79 best solution, best possible 137447.33 (44.87 seconds)\n", + "Cbc0010I After 18100 nodes, 6528 on tree, 139823.79 best solution, best possible 137450.05 (45.11 seconds)\n", + "Cbc0010I After 18200 nodes, 6576 on tree, 139823.79 best solution, best possible 137453.34 (45.33 seconds)\n", + "Cbc0010I After 18300 nodes, 6625 on tree, 139823.79 best solution, best possible 137455.86 (45.56 seconds)\n", + "Cbc0010I After 18400 nodes, 6674 on tree, 139823.79 best solution, best possible 137460.09 (45.85 seconds)\n", + "Cbc0010I After 18500 nodes, 6722 on tree, 139823.79 best solution, best possible 137460.72 (46.13 seconds)\n", + "Cbc0010I After 18600 nodes, 6769 on tree, 139823.79 best solution, best possible 137468.51 (46.38 seconds)\n", + "Cbc0010I After 18700 nodes, 6820 on tree, 139823.79 best solution, best possible 137471.08 (46.68 seconds)\n", + "Cbc0010I After 18800 nodes, 6868 on tree, 139823.79 best solution, best possible 137474.35 (46.92 seconds)\n", + "Cbc0010I After 18900 nodes, 6916 on tree, 139823.79 best solution, best possible 137478.29 (47.18 seconds)\n", + "Cbc0010I After 19000 nodes, 6965 on tree, 139823.79 best solution, best possible 137481.62 (47.43 seconds)\n", + "Cbc0010I After 19100 nodes, 7015 on tree, 139823.79 best solution, best possible 137484.41 (47.68 seconds)\n", + "Cbc0010I After 19200 nodes, 7064 on tree, 139823.79 best solution, best possible 137487.88 (47.92 seconds)\n", + "Cbc0010I After 19300 nodes, 7114 on tree, 139823.79 best solution, best possible 137491.16 (48.16 seconds)\n", + "Cbc0010I After 19400 nodes, 7162 on tree, 139823.79 best solution, best possible 137493.43 (48.40 seconds)\n", + "Cbc0010I After 19500 nodes, 7211 on tree, 139823.79 best solution, best possible 137495.32 (48.64 seconds)\n", + "Cbc0010I After 19600 nodes, 7258 on tree, 139823.79 best solution, best possible 137498.69 (48.87 seconds)\n", + "Cbc0010I After 19700 nodes, 7306 on tree, 139823.79 best solution, best possible 137501.33 (49.09 seconds)\n", + "Cbc0010I After 19800 nodes, 7357 on tree, 139823.79 best solution, best possible 137504.53 (49.37 seconds)\n", + "Cbc0010I After 19900 nodes, 7407 on tree, 139823.79 best solution, best possible 137508.07 (49.64 seconds)\n", + "Cbc0010I After 20000 nodes, 7457 on tree, 139823.79 best solution, best possible 137510.25 (49.89 seconds)\n", + "Cbc0010I After 20100 nodes, 7504 on tree, 139823.79 best solution, best possible 137513.61 (50.14 seconds)\n", + "Cbc0010I After 20200 nodes, 7554 on tree, 139823.79 best solution, best possible 137516.19 (50.41 seconds)\n", + "Cbc0010I After 20300 nodes, 7602 on tree, 139823.79 best solution, best possible 137518.87 (50.68 seconds)\n", + "Cbc0010I After 20400 nodes, 7653 on tree, 139823.79 best solution, best possible 137521.44 (50.95 seconds)\n", + "Cbc0010I After 20500 nodes, 7702 on tree, 139823.79 best solution, best possible 137524.15 (51.27 seconds)\n", + "Cbc0010I After 20600 nodes, 7752 on tree, 139823.79 best solution, best possible 137526.66 (51.53 seconds)\n", + "Cbc0010I After 20700 nodes, 7798 on tree, 139823.79 best solution, best possible 137529.57 (51.82 seconds)\n", + "Cbc0010I After 20800 nodes, 7848 on tree, 139823.79 best solution, best possible 137531.98 (52.07 seconds)\n", + "Cbc0010I After 20900 nodes, 7899 on tree, 139823.79 best solution, best possible 137534.37 (52.33 seconds)\n", + "Cbc0010I After 21000 nodes, 7947 on tree, 139823.79 best solution, best possible 137536.08 (52.54 seconds)\n", + "Cbc0010I After 21100 nodes, 7999 on tree, 139823.79 best solution, best possible 137536.11 (52.80 seconds)\n", + "Cbc0010I After 21200 nodes, 8042 on tree, 139823.79 best solution, best possible 137536.11 (53.04 seconds)\n", + "Cbc0010I After 21300 nodes, 8081 on tree, 139823.79 best solution, best possible 137536.11 (53.35 seconds)\n", + "Cbc0010I After 21400 nodes, 8125 on tree, 139823.79 best solution, best possible 137536.11 (53.70 seconds)\n", + "Cbc0010I After 21500 nodes, 8163 on tree, 139823.79 best solution, best possible 137536.11 (53.96 seconds)\n", + "Cbc0010I After 21600 nodes, 8203 on tree, 139823.79 best solution, best possible 137536.11 (54.26 seconds)\n", + "Cbc0010I After 21700 nodes, 8250 on tree, 139823.79 best solution, best possible 137536.11 (54.54 seconds)\n", + "Cbc0010I After 21800 nodes, 8292 on tree, 139823.79 best solution, best possible 137536.11 (54.85 seconds)\n", + "Cbc0010I After 21900 nodes, 8333 on tree, 139823.79 best solution, best possible 137536.11 (55.06 seconds)\n", + "Cbc0010I After 22000 nodes, 8373 on tree, 139823.79 best solution, best possible 137536.11 (55.31 seconds)\n", + "Cbc0010I After 22100 nodes, 8421 on tree, 139823.79 best solution, best possible 137538.9 (55.63 seconds)\n", + "Cbc0010I After 22200 nodes, 8471 on tree, 139823.79 best solution, best possible 137540.56 (55.91 seconds)\n", + "Cbc0010I After 22300 nodes, 8520 on tree, 139823.79 best solution, best possible 137543.46 (56.24 seconds)\n", + "Cbc0010I After 22400 nodes, 8570 on tree, 139823.79 best solution, best possible 137545.77 (56.47 seconds)\n", + "Cbc0010I After 22500 nodes, 8617 on tree, 139823.79 best solution, best possible 137548.03 (56.72 seconds)\n", + "Cbc0010I After 22600 nodes, 8669 on tree, 139823.79 best solution, best possible 137550.26 (56.98 seconds)\n", + "Cbc0010I After 22700 nodes, 8718 on tree, 139823.79 best solution, best possible 137552.67 (57.25 seconds)\n", + "Cbc0010I After 22800 nodes, 8765 on tree, 139823.79 best solution, best possible 137554.72 (57.55 seconds)\n", + "Cbc0010I After 22900 nodes, 8815 on tree, 139823.79 best solution, best possible 137556.49 (57.83 seconds)\n", + "Cbc0010I After 23000 nodes, 8863 on tree, 139823.79 best solution, best possible 137558.51 (58.13 seconds)\n", + "Cbc0010I After 23100 nodes, 8909 on tree, 139823.79 best solution, best possible 137561.04 (58.38 seconds)\n", + "Cbc0010I After 23200 nodes, 8959 on tree, 139823.79 best solution, best possible 137563.1 (58.67 seconds)\n", + "Cbc0010I After 23300 nodes, 9008 on tree, 139823.79 best solution, best possible 137565.53 (58.95 seconds)\n", + "Cbc0010I After 23400 nodes, 9059 on tree, 139823.79 best solution, best possible 137567.11 (59.33 seconds)\n", + "Cbc0010I After 23500 nodes, 9108 on tree, 139823.79 best solution, best possible 137569.6 (59.66 seconds)\n", + "Cbc0010I After 23600 nodes, 9155 on tree, 139823.79 best solution, best possible 137571.88 (59.95 seconds)\n", + "Cbc0010I After 23700 nodes, 9206 on tree, 139823.79 best solution, best possible 137573.79 (60.22 seconds)\n", + "Cbc0010I After 23800 nodes, 9255 on tree, 139823.79 best solution, best possible 137575.73 (60.51 seconds)\n", + "Cbc0010I After 23900 nodes, 9302 on tree, 139823.79 best solution, best possible 137577.86 (60.75 seconds)\n", + "Cbc0010I After 24000 nodes, 9352 on tree, 139823.79 best solution, best possible 137580.18 (61.03 seconds)\n", + "Cbc0010I After 24100 nodes, 9400 on tree, 139823.79 best solution, best possible 137581.69 (61.27 seconds)\n", + "Cbc0010I After 24200 nodes, 9450 on tree, 139823.79 best solution, best possible 137583.4 (61.52 seconds)\n", + "Cbc0010I After 24300 nodes, 9501 on tree, 139823.79 best solution, best possible 137585.59 (61.78 seconds)\n", + "Cbc0010I After 24400 nodes, 9548 on tree, 139823.79 best solution, best possible 137587.24 (62.08 seconds)\n", + "Cbc0010I After 24500 nodes, 9597 on tree, 139823.79 best solution, best possible 137589.6 (62.39 seconds)\n", + "Cbc0010I After 24600 nodes, 9647 on tree, 139823.79 best solution, best possible 137591.53 (62.74 seconds)\n", + "Cbc0010I After 24700 nodes, 9695 on tree, 139823.79 best solution, best possible 137593.1 (63.08 seconds)\n", + "Cbc0010I After 24800 nodes, 9745 on tree, 139823.79 best solution, best possible 137594.83 (63.44 seconds)\n", + "Cbc0010I After 24900 nodes, 9794 on tree, 139823.79 best solution, best possible 137596.19 (63.75 seconds)\n", + "Cbc0010I After 25000 nodes, 9843 on tree, 139823.79 best solution, best possible 137598.33 (64.07 seconds)\n", + "Cbc0010I After 25100 nodes, 9895 on tree, 139823.79 best solution, best possible 137598.35 (64.35 seconds)\n", + "Cbc0010I After 25200 nodes, 9944 on tree, 139823.79 best solution, best possible 137598.35 (64.58 seconds)\n", + "Cbc0010I After 25300 nodes, 9986 on tree, 139823.79 best solution, best possible 137598.35 (64.80 seconds)\n", + "Cbc0010I After 25400 nodes, 10025 on tree, 139823.79 best solution, best possible 137598.35 (65.07 seconds)\n", + "Cbc0010I After 25500 nodes, 10072 on tree, 139823.79 best solution, best possible 137598.35 (65.32 seconds)\n", + "Cbc0010I After 25600 nodes, 10113 on tree, 139823.79 best solution, best possible 137598.35 (65.58 seconds)\n", + "Cbc0010I After 25700 nodes, 10153 on tree, 139823.79 best solution, best possible 137598.35 (65.86 seconds)\n", + "Cbc0010I After 25800 nodes, 10195 on tree, 139823.79 best solution, best possible 137598.35 (66.18 seconds)\n", + "Cbc0010I After 25900 nodes, 10229 on tree, 139823.79 best solution, best possible 137598.35 (66.48 seconds)\n", + "Cbc0010I After 26000 nodes, 10274 on tree, 139823.79 best solution, best possible 137598.35 (66.77 seconds)\n", + "Cbc0010I After 26100 nodes, 10313 on tree, 139823.79 best solution, best possible 137598.35 (67.02 seconds)\n", + "Cbc0010I After 26200 nodes, 10356 on tree, 139823.79 best solution, best possible 137598.35 (67.34 seconds)\n", + "Cbc0010I After 26300 nodes, 10392 on tree, 139823.79 best solution, best possible 137598.35 (67.63 seconds)\n", + "Cbc0010I After 26400 nodes, 10434 on tree, 139823.79 best solution, best possible 137598.35 (67.92 seconds)\n", + "Cbc0010I After 26500 nodes, 10483 on tree, 139823.79 best solution, best possible 137598.35 (68.22 seconds)\n", + "Cbc0010I After 26600 nodes, 10527 on tree, 139823.79 best solution, best possible 137598.35 (68.48 seconds)\n", + "Cbc0010I After 26700 nodes, 10570 on tree, 139823.79 best solution, best possible 137598.35 (68.71 seconds)\n", + "Cbc0010I After 26800 nodes, 10613 on tree, 139823.79 best solution, best possible 137598.35 (68.99 seconds)\n", + "Cbc0010I After 26900 nodes, 10661 on tree, 139823.79 best solution, best possible 137598.35 (69.21 seconds)\n", + "Cbc0010I After 27000 nodes, 10696 on tree, 139823.79 best solution, best possible 137598.35 (69.46 seconds)\n", + "Cbc0010I After 27100 nodes, 10742 on tree, 139823.79 best solution, best possible 137598.35 (69.75 seconds)\n", + "Cbc0010I After 27200 nodes, 10778 on tree, 139823.79 best solution, best possible 137598.35 (70.00 seconds)\n", + "Cbc0010I After 27300 nodes, 10817 on tree, 139823.79 best solution, best possible 137598.35 (70.24 seconds)\n", + "Cbc0010I After 27400 nodes, 10864 on tree, 139823.79 best solution, best possible 137598.35 (70.49 seconds)\n", + "Cbc0010I After 27500 nodes, 10903 on tree, 139823.79 best solution, best possible 137598.35 (70.75 seconds)\n", + "Cbc0010I After 27600 nodes, 10947 on tree, 139823.79 best solution, best possible 137598.35 (70.98 seconds)\n", + "Cbc0010I After 27700 nodes, 10992 on tree, 139823.79 best solution, best possible 137598.35 (71.24 seconds)\n", + "Cbc0010I After 27800 nodes, 11029 on tree, 139823.79 best solution, best possible 137598.35 (71.48 seconds)\n", + "Cbc0010I After 27900 nodes, 11064 on tree, 139823.79 best solution, best possible 137598.35 (71.71 seconds)\n", + "Cbc0010I After 28000 nodes, 11105 on tree, 139823.79 best solution, best possible 137598.35 (71.96 seconds)\n", + "Cbc0010I After 28100 nodes, 11094 on tree, 139823.79 best solution, best possible 137598.35 (72.10 seconds)\n", + "Cbc0010I After 28200 nodes, 11088 on tree, 139823.79 best solution, best possible 137598.35 (72.23 seconds)\n", + "Cbc0010I After 28300 nodes, 11076 on tree, 139823.79 best solution, best possible 137598.35 (72.39 seconds)\n", + "Cbc0010I After 28400 nodes, 11060 on tree, 139823.79 best solution, best possible 137598.35 (72.54 seconds)\n", + "Cbc0010I After 28500 nodes, 11050 on tree, 139823.79 best solution, best possible 137598.35 (72.69 seconds)\n", + "Cbc0010I After 28600 nodes, 11035 on tree, 139823.79 best solution, best possible 137598.35 (72.85 seconds)\n", + "Cbc0010I After 28700 nodes, 11018 on tree, 139823.79 best solution, best possible 137598.35 (73.00 seconds)\n", + "Cbc0010I After 28800 nodes, 11022 on tree, 139823.79 best solution, best possible 137598.35 (73.13 seconds)\n", + "Cbc0010I After 28900 nodes, 11017 on tree, 139823.79 best solution, best possible 137598.35 (73.30 seconds)\n", + "Cbc0010I After 29000 nodes, 11018 on tree, 139823.79 best solution, best possible 137598.35 (73.47 seconds)\n", + "Cbc0010I After 29100 nodes, 11063 on tree, 139823.79 best solution, best possible 137599.82 (73.84 seconds)\n", + "Cbc0010I After 29200 nodes, 11110 on tree, 139823.79 best solution, best possible 137601.7 (74.15 seconds)\n", + "Cbc0010I After 29300 nodes, 11159 on tree, 139823.79 best solution, best possible 137603.34 (74.45 seconds)\n", + "Cbc0010I After 29400 nodes, 11208 on tree, 139823.79 best solution, best possible 137605.43 (74.79 seconds)\n", + "Cbc0010I After 29500 nodes, 11259 on tree, 139823.79 best solution, best possible 137607.27 (75.07 seconds)\n", + "Cbc0010I After 29600 nodes, 11306 on tree, 139823.79 best solution, best possible 137609.75 (75.32 seconds)\n", + "Cbc0010I After 29700 nodes, 11357 on tree, 139823.79 best solution, best possible 137610.86 (75.59 seconds)\n", + "Cbc0010I After 29800 nodes, 11407 on tree, 139823.79 best solution, best possible 137612.19 (75.89 seconds)\n", + "Cbc0010I After 29900 nodes, 11455 on tree, 139823.79 best solution, best possible 137613.71 (76.16 seconds)\n", + "Cbc0010I After 30000 nodes, 11505 on tree, 139823.79 best solution, best possible 137615.56 (76.39 seconds)\n", + "Cbc0010I After 30100 nodes, 11557 on tree, 139823.79 best solution, best possible 137616.13 (76.66 seconds)\n", + "Cbc0010I After 30200 nodes, 11602 on tree, 139823.79 best solution, best possible 137616.13 (76.94 seconds)\n", + "Cbc0010I After 30300 nodes, 11648 on tree, 139823.79 best solution, best possible 137616.13 (77.21 seconds)\n", + "Cbc0010I After 30400 nodes, 11692 on tree, 139823.79 best solution, best possible 137616.13 (77.54 seconds)\n", + "Cbc0010I After 30500 nodes, 11739 on tree, 139823.79 best solution, best possible 137616.13 (77.85 seconds)\n", + "Cbc0010I After 30600 nodes, 11783 on tree, 139823.79 best solution, best possible 137616.13 (78.13 seconds)\n", + "Cbc0010I After 30700 nodes, 11826 on tree, 139823.79 best solution, best possible 137616.13 (78.39 seconds)\n", + "Cbc0010I After 30800 nodes, 11865 on tree, 139823.79 best solution, best possible 137616.13 (78.66 seconds)\n", + "Cbc0010I After 30900 nodes, 11904 on tree, 139823.79 best solution, best possible 137616.13 (78.96 seconds)\n", + "Cbc0010I After 31000 nodes, 11948 on tree, 139823.79 best solution, best possible 137616.13 (79.22 seconds)\n", + "Cbc0010I After 31100 nodes, 11983 on tree, 139823.79 best solution, best possible 137616.13 (79.47 seconds)\n", + "Cbc0010I After 31200 nodes, 12027 on tree, 139823.79 best solution, best possible 137616.13 (79.72 seconds)\n", + "Cbc0010I After 31300 nodes, 12066 on tree, 139823.79 best solution, best possible 137616.13 (79.98 seconds)\n", + "Cbc0010I After 31400 nodes, 12107 on tree, 139823.79 best solution, best possible 137616.13 (80.23 seconds)\n", + "Cbc0010I After 31500 nodes, 12148 on tree, 139823.79 best solution, best possible 137616.13 (80.47 seconds)\n", + "Cbc0010I After 31600 nodes, 12194 on tree, 139823.79 best solution, best possible 137616.13 (80.71 seconds)\n", + "Cbc0010I After 31700 nodes, 12233 on tree, 139823.79 best solution, best possible 137616.13 (80.95 seconds)\n", + "Cbc0010I After 31800 nodes, 12275 on tree, 139823.79 best solution, best possible 137616.13 (81.21 seconds)\n", + "Cbc0010I After 31900 nodes, 12315 on tree, 139823.79 best solution, best possible 137616.13 (81.53 seconds)\n", + "Cbc0010I After 32000 nodes, 12356 on tree, 139823.79 best solution, best possible 137616.13 (81.85 seconds)\n", + "Cbc0012I Integer solution of 139780.35 found by heuristic after 2558592 iterations and 32041 nodes (81.93 seconds)\n", + "Cbc0010I After 32100 nodes, 12313 on tree, 139780.35 best solution, best possible 137616.13 (82.06 seconds)\n", + "Cbc0010I After 32200 nodes, 12339 on tree, 139780.35 best solution, best possible 137616.13 (82.26 seconds)\n", + "Cbc0010I After 32300 nodes, 12369 on tree, 139780.35 best solution, best possible 137616.13 (82.54 seconds)\n", + "Cbc0010I After 32400 nodes, 12394 on tree, 139780.35 best solution, best possible 137616.13 (82.81 seconds)\n", + "Cbc0010I After 32500 nodes, 12427 on tree, 139780.35 best solution, best possible 137616.13 (83.09 seconds)\n", + "Cbc0010I After 32600 nodes, 12452 on tree, 139780.35 best solution, best possible 137616.13 (83.33 seconds)\n", + "Cbc0010I After 32700 nodes, 12480 on tree, 139780.35 best solution, best possible 137616.13 (83.58 seconds)\n", + "Cbc0010I After 32800 nodes, 12510 on tree, 139780.35 best solution, best possible 137616.13 (83.83 seconds)\n", + "Cbc0010I After 32900 nodes, 12558 on tree, 139780.35 best solution, best possible 137616.13 (84.12 seconds)\n", + "Cbc0010I After 33000 nodes, 12588 on tree, 139780.35 best solution, best possible 137616.13 (84.39 seconds)\n", + "Cbc0010I After 33100 nodes, 12631 on tree, 139780.35 best solution, best possible 137617.29 (84.70 seconds)\n", + "Cbc0010I After 33200 nodes, 12682 on tree, 139780.35 best solution, best possible 137619.07 (85.01 seconds)\n", + "Cbc0010I After 33300 nodes, 12729 on tree, 139780.35 best solution, best possible 137620.96 (85.38 seconds)\n", + "Cbc0010I After 33400 nodes, 12778 on tree, 139780.35 best solution, best possible 137622.76 (85.66 seconds)\n", + "Cbc0010I After 33500 nodes, 12828 on tree, 139780.35 best solution, best possible 137624.11 (85.98 seconds)\n", + "Cbc0010I After 33600 nodes, 12873 on tree, 139780.35 best solution, best possible 137626.09 (86.24 seconds)\n", + "Cbc0010I After 33700 nodes, 12923 on tree, 139780.35 best solution, best possible 137627.72 (86.56 seconds)\n", + "Cbc0010I After 33800 nodes, 12973 on tree, 139780.35 best solution, best possible 137629.37 (86.85 seconds)\n", + "Cbc0010I After 33900 nodes, 13023 on tree, 139780.35 best solution, best possible 137630.88 (87.15 seconds)\n", + "Cbc0010I After 34000 nodes, 13072 on tree, 139780.35 best solution, best possible 137632.81 (87.46 seconds)\n", + "Cbc0010I After 34100 nodes, 13119 on tree, 139780.35 best solution, best possible 137633.08 (87.70 seconds)\n", + "Cbc0010I After 34200 nodes, 13166 on tree, 139780.35 best solution, best possible 137633.08 (87.97 seconds)\n", + "Cbc0010I After 34300 nodes, 13217 on tree, 139780.35 best solution, best possible 137633.08 (88.22 seconds)\n", + "Cbc0010I After 34400 nodes, 13266 on tree, 139780.35 best solution, best possible 137633.08 (88.48 seconds)\n", + "Cbc0010I After 34500 nodes, 13313 on tree, 139780.35 best solution, best possible 137633.08 (88.75 seconds)\n", + "Cbc0010I After 34600 nodes, 13363 on tree, 139780.35 best solution, best possible 137633.08 (89.01 seconds)\n", + "Cbc0010I After 34700 nodes, 13408 on tree, 139780.35 best solution, best possible 137633.08 (89.28 seconds)\n", + "Cbc0010I After 34800 nodes, 13455 on tree, 139780.35 best solution, best possible 137633.08 (89.56 seconds)\n", + "Cbc0010I After 34900 nodes, 13505 on tree, 139780.35 best solution, best possible 137633.08 (89.88 seconds)\n", + "Cbc0010I After 35000 nodes, 13551 on tree, 139780.35 best solution, best possible 137633.08 (90.16 seconds)\n", + "Cbc0010I After 35100 nodes, 13600 on tree, 139780.35 best solution, best possible 137633.08 (90.45 seconds)\n", + "Cbc0010I After 35200 nodes, 13641 on tree, 139780.35 best solution, best possible 137633.08 (90.80 seconds)\n", + "Cbc0010I After 35300 nodes, 13685 on tree, 139780.35 best solution, best possible 137633.08 (91.07 seconds)\n", + "Cbc0010I After 35400 nodes, 13734 on tree, 139780.35 best solution, best possible 137633.08 (91.40 seconds)\n", + "Cbc0010I After 35500 nodes, 13782 on tree, 139780.35 best solution, best possible 137633.08 (91.72 seconds)\n", + "Cbc0010I After 35600 nodes, 13823 on tree, 139780.35 best solution, best possible 137633.08 (92.01 seconds)\n", + "Cbc0010I After 35700 nodes, 13864 on tree, 139780.35 best solution, best possible 137633.08 (92.28 seconds)\n", + "Cbc0010I After 35800 nodes, 13907 on tree, 139780.35 best solution, best possible 137633.08 (92.56 seconds)\n", + "Cbc0010I After 35900 nodes, 13948 on tree, 139780.35 best solution, best possible 137633.08 (92.82 seconds)\n", + "Cbc0010I After 36000 nodes, 13983 on tree, 139780.35 best solution, best possible 137633.08 (93.09 seconds)\n", + "Cbc0010I After 36100 nodes, 13966 on tree, 139780.35 best solution, best possible 137633.08 (93.29 seconds)\n", + "Cbc0010I After 36200 nodes, 13960 on tree, 139780.35 best solution, best possible 137633.08 (93.55 seconds)\n", + "Cbc0010I After 36300 nodes, 13953 on tree, 139780.35 best solution, best possible 137633.08 (93.75 seconds)\n", + "Cbc0010I After 36400 nodes, 13951 on tree, 139780.35 best solution, best possible 137633.08 (93.96 seconds)\n", + "Cbc0010I After 36500 nodes, 13945 on tree, 139780.35 best solution, best possible 137633.08 (94.15 seconds)\n", + "Cbc0010I After 36600 nodes, 13936 on tree, 139780.35 best solution, best possible 137633.08 (94.37 seconds)\n", + "Cbc0012I Integer solution of 139774.7 found by heuristic after 2920950 iterations and 36680 nodes (94.51 seconds)\n", + "Cbc0010I After 36700 nodes, 13924 on tree, 139774.7 best solution, best possible 137633.08 (94.55 seconds)\n", + "Cbc0010I After 36800 nodes, 13948 on tree, 139774.7 best solution, best possible 137633.08 (94.81 seconds)\n", + "Cbc0010I After 36900 nodes, 13972 on tree, 139774.7 best solution, best possible 137633.08 (95.07 seconds)\n", + "Cbc0010I After 37000 nodes, 13991 on tree, 139774.7 best solution, best possible 137633.08 (95.27 seconds)\n", + "Cbc0010I After 37100 nodes, 13985 on tree, 139774.7 best solution, best possible 137633.08 (95.50 seconds)\n", + "Cbc0010I After 37200 nodes, 13970 on tree, 139774.7 best solution, best possible 137633.08 (95.68 seconds)\n", + "Cbc0010I After 37300 nodes, 13972 on tree, 139774.7 best solution, best possible 137633.08 (95.88 seconds)\n", + "Cbc0010I After 37400 nodes, 13954 on tree, 139774.7 best solution, best possible 137633.08 (96.07 seconds)\n", + "Cbc0012I Integer solution of 139652.05 found by heuristic after 2967627 iterations and 37418 nodes (96.10 seconds)\n", + "Cbc0010I After 37500 nodes, 13695 on tree, 139652.05 best solution, best possible 137633.08 (96.29 seconds)\n", + "Cbc0010I After 37600 nodes, 13743 on tree, 139652.05 best solution, best possible 137633.08 (96.54 seconds)\n", + "Cbc0010I After 37700 nodes, 13770 on tree, 139652.05 best solution, best possible 137633.08 (96.75 seconds)\n", + "Cbc0010I After 37800 nodes, 13808 on tree, 139652.05 best solution, best possible 137633.08 (96.97 seconds)\n", + "Cbc0010I After 37900 nodes, 13850 on tree, 139652.05 best solution, best possible 137633.08 (97.19 seconds)\n", + "Cbc0010I After 38000 nodes, 13881 on tree, 139652.05 best solution, best possible 137633.08 (97.42 seconds)\n", + "Cbc0010I After 38100 nodes, 13887 on tree, 139652.05 best solution, best possible 137633.08 (97.58 seconds)\n", + "Cbc0010I After 38200 nodes, 13859 on tree, 139652.05 best solution, best possible 137633.08 (97.74 seconds)\n", + "Cbc0010I After 38300 nodes, 13859 on tree, 139652.05 best solution, best possible 137633.08 (97.92 seconds)\n", + "Cbc0010I After 38400 nodes, 13867 on tree, 139652.05 best solution, best possible 137633.08 (98.09 seconds)\n", + "Cbc0012I Integer solution of 139485.54 found by heuristic after 3028696 iterations and 38437 nodes (98.16 seconds)\n", + "Cbc0010I After 38500 nodes, 13470 on tree, 139485.54 best solution, best possible 137633.08 (98.33 seconds)\n", + "Cbc0010I After 38600 nodes, 13519 on tree, 139485.54 best solution, best possible 137633.08 (98.63 seconds)\n", + "Cbc0010I After 38700 nodes, 13566 on tree, 139485.54 best solution, best possible 137633.08 (98.91 seconds)\n", + "Cbc0010I After 38800 nodes, 13612 on tree, 139485.54 best solution, best possible 137633.08 (99.15 seconds)\n", + "Cbc0010I After 38900 nodes, 13640 on tree, 139485.54 best solution, best possible 137633.08 (99.32 seconds)\n", + "Cbc0010I After 39000 nodes, 13655 on tree, 139485.54 best solution, best possible 137633.08 (99.49 seconds)\n", + "Cbc0010I After 39100 nodes, 13683 on tree, 139485.54 best solution, best possible 137633.08 (99.69 seconds)\n", + "Cbc0010I After 39200 nodes, 13726 on tree, 139485.54 best solution, best possible 137633.08 (99.97 seconds)\n", + "Cbc0010I After 39300 nodes, 13771 on tree, 139485.54 best solution, best possible 137633.08 (100.27 seconds)\n", + "Cbc0010I After 39400 nodes, 13822 on tree, 139485.54 best solution, best possible 137633.08 (100.62 seconds)\n", + "Cbc0010I After 39500 nodes, 13869 on tree, 139485.54 best solution, best possible 137633.08 (100.94 seconds)\n", + "Cbc0010I After 39600 nodes, 13915 on tree, 139485.54 best solution, best possible 137633.08 (101.23 seconds)\n", + "Cbc0010I After 39700 nodes, 13962 on tree, 139485.54 best solution, best possible 137633.08 (101.55 seconds)\n", + "Cbc0010I After 39800 nodes, 14009 on tree, 139485.54 best solution, best possible 137633.08 (101.84 seconds)\n", + "Cbc0010I After 39900 nodes, 14058 on tree, 139485.54 best solution, best possible 137633.08 (102.10 seconds)\n", + "Cbc0010I After 40000 nodes, 14106 on tree, 139485.54 best solution, best possible 137633.08 (102.39 seconds)\n", + "Cbc0010I After 40100 nodes, 14087 on tree, 139485.54 best solution, best possible 137633.08 (102.56 seconds)\n", + "Cbc0010I After 40200 nodes, 14076 on tree, 139485.54 best solution, best possible 137633.08 (102.73 seconds)\n", + "Cbc0010I After 40300 nodes, 14077 on tree, 139485.54 best solution, best possible 137633.08 (102.89 seconds)\n", + "Cbc0010I After 40400 nodes, 14076 on tree, 139485.54 best solution, best possible 137633.08 (103.12 seconds)\n", + "Cbc0010I After 40500 nodes, 14069 on tree, 139485.54 best solution, best possible 137633.08 (103.29 seconds)\n", + "Cbc0010I After 40600 nodes, 14045 on tree, 139485.54 best solution, best possible 137633.08 (103.45 seconds)\n", + "Cbc0012I Integer solution of 139485.54 found by heuristic after 3182299 iterations and 40693 nodes (103.68 seconds)\n", + "Cbc0010I After 40700 nodes, 14058 on tree, 139485.54 best solution, best possible 137633.08 (103.69 seconds)\n", + "Cbc0010I After 40800 nodes, 14072 on tree, 139485.54 best solution, best possible 137633.08 (103.87 seconds)\n", + "Cbc0010I After 40900 nodes, 14115 on tree, 139485.54 best solution, best possible 137633.08 (104.12 seconds)\n", + "Cbc0010I After 41000 nodes, 14159 on tree, 139485.54 best solution, best possible 137633.08 (104.42 seconds)\n", + "Cbc0010I After 41100 nodes, 14143 on tree, 139485.54 best solution, best possible 137633.08 (104.61 seconds)\n", + "Cbc0010I After 41200 nodes, 14119 on tree, 139485.54 best solution, best possible 137633.08 (104.80 seconds)\n", + "Cbc0010I After 41300 nodes, 14117 on tree, 139485.54 best solution, best possible 137633.08 (104.99 seconds)\n", + "Cbc0010I After 41400 nodes, 14122 on tree, 139485.54 best solution, best possible 137633.08 (105.19 seconds)\n", + "Cbc0010I After 41500 nodes, 14116 on tree, 139485.54 best solution, best possible 137633.08 (105.42 seconds)\n", + "Cbc0010I After 41600 nodes, 14070 on tree, 139485.54 best solution, best possible 137633.08 (105.58 seconds)\n", + "Cbc0010I After 41700 nodes, 14082 on tree, 139485.54 best solution, best possible 137633.08 (105.82 seconds)\n", + "Cbc0010I After 41800 nodes, 14078 on tree, 139485.54 best solution, best possible 137633.08 (106.05 seconds)\n", + "Cbc0010I After 41900 nodes, 14078 on tree, 139485.54 best solution, best possible 137633.08 (106.24 seconds)\n", + "Cbc0010I After 42000 nodes, 14025 on tree, 139485.54 best solution, best possible 137633.08 (106.41 seconds)\n", + "Cbc0010I After 42100 nodes, 14016 on tree, 139485.54 best solution, best possible 137633.08 (106.64 seconds)\n", + "Cbc0010I After 42200 nodes, 14027 on tree, 139485.54 best solution, best possible 137633.08 (106.86 seconds)\n", + "Cbc0010I After 42300 nodes, 14027 on tree, 139485.54 best solution, best possible 137633.08 (107.07 seconds)\n", + "Cbc0010I After 42400 nodes, 13997 on tree, 139485.54 best solution, best possible 137633.08 (107.28 seconds)\n", + "Cbc0010I After 42500 nodes, 13963 on tree, 139485.54 best solution, best possible 137633.08 (107.45 seconds)\n", + "Cbc0010I After 42600 nodes, 13960 on tree, 139485.54 best solution, best possible 137633.08 (107.66 seconds)\n", + "Cbc0010I After 42700 nodes, 13969 on tree, 139485.54 best solution, best possible 137633.08 (107.83 seconds)\n", + "Cbc0010I After 42800 nodes, 13957 on tree, 139485.54 best solution, best possible 137633.08 (108.01 seconds)\n", + "Cbc0010I After 42900 nodes, 13909 on tree, 139485.54 best solution, best possible 137633.08 (108.20 seconds)\n", + "Cbc0010I After 43000 nodes, 13905 on tree, 139485.54 best solution, best possible 137633.08 (108.44 seconds)\n", + "Cbc0010I After 43100 nodes, 13921 on tree, 139485.54 best solution, best possible 137633.08 (108.65 seconds)\n", + "Cbc0010I After 43200 nodes, 13907 on tree, 139485.54 best solution, best possible 137633.08 (108.83 seconds)\n", + "Cbc0010I After 43300 nodes, 13906 on tree, 139485.54 best solution, best possible 137633.08 (109.05 seconds)\n", + "Cbc0010I After 43400 nodes, 13908 on tree, 139485.54 best solution, best possible 137633.08 (109.31 seconds)\n", + "Cbc0010I After 43500 nodes, 13883 on tree, 139485.54 best solution, best possible 137633.08 (109.49 seconds)\n", + "Cbc0010I After 43600 nodes, 13870 on tree, 139485.54 best solution, best possible 137633.08 (109.67 seconds)\n", + "Cbc0010I After 43700 nodes, 13866 on tree, 139485.54 best solution, best possible 137633.08 (109.88 seconds)\n", + "Cbc0010I After 43800 nodes, 13855 on tree, 139485.54 best solution, best possible 137633.08 (110.08 seconds)\n", + "Cbc0010I After 43900 nodes, 13827 on tree, 139485.54 best solution, best possible 137633.08 (110.24 seconds)\n", + "Cbc0010I After 44000 nodes, 13832 on tree, 139485.54 best solution, best possible 137633.08 (110.40 seconds)\n", + "Cbc0010I After 44100 nodes, 13837 on tree, 139485.54 best solution, best possible 137633.08 (110.61 seconds)\n", + "Cbc0012I Integer solution of 139485.54 found by heuristic after 3390346 iterations and 44145 nodes (110.68 seconds)\n", + "Cbc0010I After 44200 nodes, 13849 on tree, 139485.54 best solution, best possible 137633.08 (110.84 seconds)\n", + "Cbc0010I After 44300 nodes, 13895 on tree, 139485.54 best solution, best possible 137633.08 (111.19 seconds)\n", + "Cbc0010I After 44400 nodes, 13941 on tree, 139485.54 best solution, best possible 137633.08 (111.47 seconds)\n", + "Cbc0010I After 44500 nodes, 13954 on tree, 139485.54 best solution, best possible 137633.08 (111.71 seconds)\n", + "Cbc0010I After 44600 nodes, 13982 on tree, 139485.54 best solution, best possible 137633.08 (111.99 seconds)\n", + "Cbc0010I After 44700 nodes, 14029 on tree, 139485.54 best solution, best possible 137633.08 (112.24 seconds)\n", + "Cbc0010I After 44800 nodes, 14067 on tree, 139485.54 best solution, best possible 137633.08 (112.45 seconds)\n", + "Cbc0010I After 44900 nodes, 14108 on tree, 139485.54 best solution, best possible 137633.08 (112.69 seconds)\n", + "Cbc0010I After 45000 nodes, 14151 on tree, 139485.54 best solution, best possible 137633.08 (112.98 seconds)\n", + "Cbc0010I After 45100 nodes, 14172 on tree, 139485.54 best solution, best possible 137633.08 (113.17 seconds)\n", + "Cbc0010I After 45200 nodes, 14160 on tree, 139485.54 best solution, best possible 137633.08 (113.32 seconds)\n", + "Cbc0010I After 45300 nodes, 14164 on tree, 139485.54 best solution, best possible 137633.08 (113.52 seconds)\n", + "Cbc0010I After 45400 nodes, 14159 on tree, 139485.54 best solution, best possible 137633.08 (113.68 seconds)\n", + "Cbc0010I After 45500 nodes, 14160 on tree, 139485.54 best solution, best possible 137633.08 (113.85 seconds)\n", + "Cbc0010I After 45600 nodes, 14158 on tree, 139485.54 best solution, best possible 137633.08 (114.05 seconds)\n", + "Cbc0010I After 45700 nodes, 14155 on tree, 139485.54 best solution, best possible 137633.08 (114.24 seconds)\n", + "Cbc0010I After 45800 nodes, 14156 on tree, 139485.54 best solution, best possible 137633.08 (114.42 seconds)\n", + "Cbc0010I After 45900 nodes, 14148 on tree, 139485.54 best solution, best possible 137633.08 (114.60 seconds)\n", + "Cbc0010I After 46000 nodes, 14125 on tree, 139485.54 best solution, best possible 137633.08 (114.76 seconds)\n", + "Cbc0010I After 46100 nodes, 14119 on tree, 139485.54 best solution, best possible 137633.08 (114.97 seconds)\n", + "Cbc0010I After 46200 nodes, 14079 on tree, 139485.54 best solution, best possible 137633.08 (115.13 seconds)\n", + "Cbc0010I After 46300 nodes, 14076 on tree, 139485.54 best solution, best possible 137633.08 (115.30 seconds)\n", + "Cbc0010I After 46400 nodes, 14078 on tree, 139485.54 best solution, best possible 137633.08 (115.49 seconds)\n", + "Cbc0010I After 46500 nodes, 14076 on tree, 139485.54 best solution, best possible 137633.08 (115.69 seconds)\n", + "Cbc0010I After 46600 nodes, 14079 on tree, 139485.54 best solution, best possible 137633.08 (115.91 seconds)\n", + "Cbc0010I After 46700 nodes, 14077 on tree, 139485.54 best solution, best possible 137633.08 (116.19 seconds)\n", + "Cbc0010I After 46800 nodes, 14079 on tree, 139485.54 best solution, best possible 137633.08 (116.47 seconds)\n", + "Cbc0010I After 46900 nodes, 14068 on tree, 139485.54 best solution, best possible 137633.08 (116.74 seconds)\n", + "Cbc0010I After 47000 nodes, 14066 on tree, 139485.54 best solution, best possible 137633.08 (116.99 seconds)\n", + "Cbc0010I After 47100 nodes, 14050 on tree, 139485.54 best solution, best possible 137633.08 (117.17 seconds)\n", + "Cbc0010I After 47200 nodes, 14035 on tree, 139485.54 best solution, best possible 137633.08 (117.41 seconds)\n", + "Cbc0010I After 47300 nodes, 13997 on tree, 139485.54 best solution, best possible 137633.08 (117.59 seconds)\n", + "Cbc0010I After 47400 nodes, 14007 on tree, 139485.54 best solution, best possible 137633.08 (117.76 seconds)\n", + "Cbc0010I After 47500 nodes, 14001 on tree, 139485.54 best solution, best possible 137633.08 (117.95 seconds)\n", + "Cbc0010I After 47600 nodes, 14003 on tree, 139485.54 best solution, best possible 137633.08 (118.14 seconds)\n", + "Cbc0010I After 47700 nodes, 14003 on tree, 139485.54 best solution, best possible 137633.08 (118.31 seconds)\n", + "Cbc0010I After 47800 nodes, 14000 on tree, 139485.54 best solution, best possible 137633.08 (118.53 seconds)\n", + "Cbc0010I After 47900 nodes, 14015 on tree, 139485.54 best solution, best possible 137633.08 (118.76 seconds)\n", + "Cbc0010I After 48000 nodes, 14026 on tree, 139485.54 best solution, best possible 137633.08 (118.97 seconds)\n", + "Cbc0010I After 48100 nodes, 13999 on tree, 139485.54 best solution, best possible 137633.08 (119.19 seconds)\n", + "Cbc0010I After 48200 nodes, 13999 on tree, 139485.54 best solution, best possible 137633.08 (119.46 seconds)\n", + "Cbc0010I After 48300 nodes, 14001 on tree, 139485.54 best solution, best possible 137633.08 (119.73 seconds)\n", + "Cbc0010I After 48400 nodes, 14008 on tree, 139485.54 best solution, best possible 137633.08 (119.94 seconds)\n", + "Cbc0010I After 48500 nodes, 14001 on tree, 139485.54 best solution, best possible 137633.08 (120.17 seconds)\n", + "Cbc0010I After 48600 nodes, 14007 on tree, 139485.54 best solution, best possible 137633.08 (120.40 seconds)\n", + "Cbc0010I After 48700 nodes, 14007 on tree, 139485.54 best solution, best possible 137633.08 (120.58 seconds)\n", + "Cbc0010I After 48800 nodes, 14008 on tree, 139485.54 best solution, best possible 137633.08 (120.76 seconds)\n", + "Cbc0010I After 48900 nodes, 14003 on tree, 139485.54 best solution, best possible 137633.08 (120.93 seconds)\n", + "Cbc0010I After 49000 nodes, 13984 on tree, 139485.54 best solution, best possible 137633.08 (121.09 seconds)\n", + "Cbc0010I After 49100 nodes, 13969 on tree, 139485.54 best solution, best possible 137633.08 (121.25 seconds)\n", + "Cbc0010I After 49200 nodes, 13944 on tree, 139485.54 best solution, best possible 137633.08 (121.42 seconds)\n", + "Cbc0010I After 49300 nodes, 13916 on tree, 139485.54 best solution, best possible 137633.08 (121.63 seconds)\n", + "Cbc0010I After 49400 nodes, 13894 on tree, 139485.54 best solution, best possible 137633.08 (121.84 seconds)\n", + "Cbc0010I After 49500 nodes, 13893 on tree, 139485.54 best solution, best possible 137633.08 (122.06 seconds)\n", + "Cbc0010I After 49600 nodes, 13871 on tree, 139485.54 best solution, best possible 6.6460908e-311 (122.20 seconds)\n", + "Cbc0010I After 49700 nodes, 13856 on tree, 139485.54 best solution, best possible 137633.08 (122.38 seconds)\n", + "Cbc0010I After 49800 nodes, 13863 on tree, 139485.54 best solution, best possible 137633.08 (122.57 seconds)\n", + "Cbc0010I After 49900 nodes, 13863 on tree, 139485.54 best solution, best possible 137633.08 (122.77 seconds)\n", + "Cbc0010I After 50000 nodes, 13867 on tree, 139485.54 best solution, best possible 137633.08 (122.99 seconds)\n", + "Cbc0010I After 50100 nodes, 13872 on tree, 139485.54 best solution, best possible 137633.08 (123.25 seconds)\n", + "Cbc0010I After 50200 nodes, 13867 on tree, 139485.54 best solution, best possible 137633.08 (123.49 seconds)\n", + "Cbc0010I After 50300 nodes, 13871 on tree, 139485.54 best solution, best possible 137633.08 (123.78 seconds)\n", + "Cbc0010I After 50400 nodes, 13862 on tree, 139485.54 best solution, best possible 137633.08 (124.04 seconds)\n", + "Cbc0010I After 50500 nodes, 13880 on tree, 139485.54 best solution, best possible 137633.08 (124.45 seconds)\n", + "Cbc0010I After 50600 nodes, 13864 on tree, 139485.54 best solution, best possible 137633.08 (124.70 seconds)\n", + "Cbc0010I After 50700 nodes, 13862 on tree, 139485.54 best solution, best possible 137633.08 (124.91 seconds)\n", + "Cbc0010I After 50800 nodes, 13866 on tree, 139485.54 best solution, best possible 137633.08 (125.16 seconds)\n", + "Cbc0010I After 50900 nodes, 13862 on tree, 139485.54 best solution, best possible 137633.08 (125.47 seconds)\n", + "Cbc0010I After 51000 nodes, 13870 on tree, 139485.54 best solution, best possible 137633.08 (125.71 seconds)\n", + "Cbc0010I After 51100 nodes, 13864 on tree, 139485.54 best solution, best possible 137633.08 (125.92 seconds)\n", + "Cbc0010I After 51200 nodes, 13844 on tree, 139485.54 best solution, best possible 137633.08 (126.13 seconds)\n", + "Cbc0010I After 51300 nodes, 13810 on tree, 139485.54 best solution, best possible 137633.08 (126.35 seconds)\n", + "Cbc0010I After 51400 nodes, 13773 on tree, 139485.54 best solution, best possible 137633.08 (126.61 seconds)\n", + "Cbc0010I After 51500 nodes, 13752 on tree, 139485.54 best solution, best possible 137633.08 (126.81 seconds)\n", + "Cbc0010I After 51600 nodes, 13775 on tree, 139485.54 best solution, best possible 137633.08 (127.03 seconds)\n", + "Cbc0010I After 51700 nodes, 13749 on tree, 139485.54 best solution, best possible 137633.08 (127.20 seconds)\n", + "Cbc0010I After 51800 nodes, 13749 on tree, 139485.54 best solution, best possible 137633.08 (127.45 seconds)\n", + "Cbc0010I After 51900 nodes, 13758 on tree, 139485.54 best solution, best possible 137633.08 (127.67 seconds)\n", + "Cbc0010I After 52000 nodes, 13765 on tree, 139485.54 best solution, best possible 137633.08 (127.93 seconds)\n", + "Cbc0010I After 52100 nodes, 13757 on tree, 139485.54 best solution, best possible 137633.08 (128.08 seconds)\n", + "Cbc0010I After 52200 nodes, 13742 on tree, 139485.54 best solution, best possible 137633.08 (128.23 seconds)\n", + "Cbc0010I After 52300 nodes, 13737 on tree, 139485.54 best solution, best possible 137633.08 (128.41 seconds)\n", + "Cbc0010I After 52400 nodes, 13722 on tree, 139485.54 best solution, best possible 137633.08 (128.57 seconds)\n", + "Cbc0010I After 52500 nodes, 13724 on tree, 139485.54 best solution, best possible 137633.08 (128.75 seconds)\n", + "Cbc0010I After 52600 nodes, 13716 on tree, 139485.54 best solution, best possible 137633.08 (128.93 seconds)\n", + "Cbc0010I After 52700 nodes, 13721 on tree, 139485.54 best solution, best possible 137633.08 (129.15 seconds)\n", + "Cbc0010I After 52800 nodes, 13715 on tree, 139485.54 best solution, best possible 137633.08 (129.34 seconds)\n", + "Cbc0010I After 52900 nodes, 13710 on tree, 139485.54 best solution, best possible 137633.08 (129.54 seconds)\n", + "Cbc0010I After 53000 nodes, 13701 on tree, 139485.54 best solution, best possible 137633.08 (129.73 seconds)\n", + "Cbc0010I After 53100 nodes, 13747 on tree, 139485.54 best solution, best possible 137634.24 (130.17 seconds)\n", + "Cbc0010I After 53200 nodes, 13797 on tree, 139485.54 best solution, best possible 137636.45 (130.61 seconds)\n", + "Cbc0010I After 53300 nodes, 13845 on tree, 139485.54 best solution, best possible 137637.81 (131.03 seconds)\n", + "Cbc0010I After 53400 nodes, 13891 on tree, 139485.54 best solution, best possible 137640.15 (131.41 seconds)\n", + "Cbc0010I After 53500 nodes, 13941 on tree, 139485.54 best solution, best possible 137642.38 (131.82 seconds)\n", + "Cbc0010I After 53600 nodes, 13989 on tree, 139485.54 best solution, best possible 137643.88 (132.14 seconds)\n", + "Cbc0010I After 53700 nodes, 14039 on tree, 139485.54 best solution, best possible 137646.15 (132.51 seconds)\n", + "Cbc0010I After 53800 nodes, 14088 on tree, 139485.54 best solution, best possible 137647.56 (132.86 seconds)\n", + "Cbc0010I After 53900 nodes, 14136 on tree, 139485.54 best solution, best possible 137649.15 (133.25 seconds)\n", + "Cbc0010I After 54000 nodes, 14184 on tree, 139485.54 best solution, best possible 137650.94 (133.58 seconds)\n", + "Cbc0010I After 54100 nodes, 14176 on tree, 139485.54 best solution, best possible 137651.19 (133.83 seconds)\n", + "Cbc0010I After 54200 nodes, 14160 on tree, 139485.54 best solution, best possible 137651.19 (133.99 seconds)\n", + "Cbc0010I After 54300 nodes, 14165 on tree, 139485.54 best solution, best possible 137651.19 (134.20 seconds)\n", + "Cbc0010I After 54400 nodes, 14147 on tree, 139485.54 best solution, best possible 137651.19 (134.44 seconds)\n", + "Cbc0010I After 54500 nodes, 14143 on tree, 139485.54 best solution, best possible 137651.19 (134.70 seconds)\n", + "Cbc0010I After 54600 nodes, 14146 on tree, 139485.54 best solution, best possible 137651.19 (135.04 seconds)\n", + "Cbc0010I After 54700 nodes, 14141 on tree, 139485.54 best solution, best possible 137651.19 (135.32 seconds)\n", + "Cbc0010I After 54800 nodes, 14146 on tree, 139485.54 best solution, best possible 137651.19 (135.60 seconds)\n", + "Cbc0010I After 54900 nodes, 14162 on tree, 139485.54 best solution, best possible 137651.19 (135.89 seconds)\n", + "Cbc0010I After 55000 nodes, 14153 on tree, 139485.54 best solution, best possible 137651.19 (136.11 seconds)\n", + "Cbc0010I After 55100 nodes, 14146 on tree, 139485.54 best solution, best possible 137651.19 (136.32 seconds)\n", + "Cbc0010I After 55200 nodes, 14144 on tree, 139485.54 best solution, best possible 137651.19 (136.48 seconds)\n", + "Cbc0010I After 55300 nodes, 14110 on tree, 139485.54 best solution, best possible 137651.19 (136.66 seconds)\n", + "Cbc0010I After 55400 nodes, 14116 on tree, 139485.54 best solution, best possible 137651.19 (136.90 seconds)\n", + "Cbc0010I After 55500 nodes, 14110 on tree, 139485.54 best solution, best possible 137651.19 (137.10 seconds)\n", + "Cbc0010I After 55600 nodes, 14086 on tree, 139485.54 best solution, best possible 137651.19 (137.29 seconds)\n", + "Cbc0010I After 55700 nodes, 14081 on tree, 139485.54 best solution, best possible 137651.19 (137.60 seconds)\n", + "Cbc0010I After 55800 nodes, 14067 on tree, 139485.54 best solution, best possible 137651.19 (137.89 seconds)\n", + "Cbc0010I After 55900 nodes, 14055 on tree, 139485.54 best solution, best possible 137651.19 (138.12 seconds)\n", + "Cbc0010I After 56000 nodes, 14039 on tree, 139485.54 best solution, best possible 137651.19 (138.33 seconds)\n", + "Cbc0010I After 56100 nodes, 14030 on tree, 139485.54 best solution, best possible 137651.19 (138.58 seconds)\n", + "Cbc0010I After 56200 nodes, 14040 on tree, 139485.54 best solution, best possible 137651.19 (138.83 seconds)\n", + "Cbc0010I After 56300 nodes, 14040 on tree, 139485.54 best solution, best possible 137651.19 (139.06 seconds)\n", + "Cbc0010I After 56400 nodes, 14037 on tree, 139485.54 best solution, best possible 137651.19 (139.26 seconds)\n", + "Cbc0010I After 56500 nodes, 14036 on tree, 139485.54 best solution, best possible 137651.19 (139.53 seconds)\n", + "Cbc0010I After 56600 nodes, 14033 on tree, 139485.54 best solution, best possible 137651.19 (139.80 seconds)\n", + "Cbc0010I After 56700 nodes, 14052 on tree, 139485.54 best solution, best possible 137651.19 (140.07 seconds)\n", + "Cbc0010I After 56800 nodes, 14040 on tree, 139485.54 best solution, best possible 137651.19 (140.41 seconds)\n", + "Cbc0010I After 56900 nodes, 14035 on tree, 139485.54 best solution, best possible 137651.19 (140.69 seconds)\n", + "Cbc0010I After 57000 nodes, 14033 on tree, 139485.54 best solution, best possible 137651.19 (141.07 seconds)\n", + "Cbc0010I After 57100 nodes, 14040 on tree, 139485.54 best solution, best possible 137651.19 (141.40 seconds)\n", + "Cbc0010I After 57200 nodes, 14040 on tree, 139485.54 best solution, best possible 137651.19 (141.61 seconds)\n", + "Cbc0010I After 57300 nodes, 14031 on tree, 139485.54 best solution, best possible 137651.19 (141.89 seconds)\n", + "Cbc0010I After 57400 nodes, 14029 on tree, 139485.54 best solution, best possible 137651.19 (142.20 seconds)\n", + "Cbc0010I After 57500 nodes, 14033 on tree, 139485.54 best solution, best possible 137651.19 (142.46 seconds)\n", + "Cbc0010I After 57600 nodes, 14036 on tree, 139485.54 best solution, best possible 137651.19 (142.64 seconds)\n", + "Cbc0010I After 57700 nodes, 14006 on tree, 139485.54 best solution, best possible 137651.19 (142.89 seconds)\n", + "Cbc0010I After 57800 nodes, 13970 on tree, 139485.54 best solution, best possible 137651.19 (143.13 seconds)\n", + "Cbc0010I After 57900 nodes, 13943 on tree, 139485.54 best solution, best possible 137651.19 (143.37 seconds)\n", + "Cbc0010I After 58000 nodes, 13933 on tree, 139485.54 best solution, best possible 137651.19 (143.63 seconds)\n", + "Cbc0010I After 58100 nodes, 13927 on tree, 139485.54 best solution, best possible 137651.19 (143.83 seconds)\n", + "Cbc0010I After 58200 nodes, 13921 on tree, 139485.54 best solution, best possible 137651.19 (144.03 seconds)\n", + "Cbc0010I After 58300 nodes, 13916 on tree, 139485.54 best solution, best possible 137651.19 (144.22 seconds)\n", + "Cbc0010I After 58400 nodes, 13920 on tree, 139485.54 best solution, best possible 137651.19 (144.42 seconds)\n", + "Cbc0010I After 58500 nodes, 13928 on tree, 139485.54 best solution, best possible 137651.19 (144.60 seconds)\n", + "Cbc0010I After 58600 nodes, 13919 on tree, 139485.54 best solution, best possible 137651.19 (144.79 seconds)\n", + "Cbc0010I After 58700 nodes, 13917 on tree, 139485.54 best solution, best possible 137651.19 (145.04 seconds)\n", + "Cbc0010I After 58800 nodes, 13911 on tree, 139485.54 best solution, best possible 137651.19 (145.31 seconds)\n", + "Cbc0010I After 58900 nodes, 13921 on tree, 139485.54 best solution, best possible 137651.19 (145.68 seconds)\n", + "Cbc0010I After 59000 nodes, 13920 on tree, 139485.54 best solution, best possible 137651.19 (146.00 seconds)\n", + "Cbc0010I After 59100 nodes, 13916 on tree, 139485.54 best solution, best possible 137651.19 (146.28 seconds)\n", + "Cbc0010I After 59200 nodes, 13931 on tree, 139485.54 best solution, best possible 137651.19 (146.51 seconds)\n", + "Cbc0010I After 59300 nodes, 13921 on tree, 139485.54 best solution, best possible 137651.19 (146.78 seconds)\n", + "Cbc0010I After 59400 nodes, 13917 on tree, 139485.54 best solution, best possible 137651.19 (147.03 seconds)\n", + "Cbc0010I After 59500 nodes, 13929 on tree, 139485.54 best solution, best possible 137651.19 (147.26 seconds)\n", + "Cbc0010I After 59600 nodes, 13920 on tree, 139485.54 best solution, best possible 137651.19 (147.48 seconds)\n", + "Cbc0010I After 59700 nodes, 13913 on tree, 139485.54 best solution, best possible 137651.19 (147.70 seconds)\n", + "Cbc0010I After 59800 nodes, 13907 on tree, 139485.54 best solution, best possible 137651.19 (147.91 seconds)\n", + "Cbc0010I After 59900 nodes, 13880 on tree, 139485.54 best solution, best possible 137651.19 (148.11 seconds)\n", + "Cbc0010I After 60000 nodes, 13885 on tree, 139485.54 best solution, best possible 137651.19 (148.29 seconds)\n", + "Cbc0010I After 60100 nodes, 13853 on tree, 139485.54 best solution, best possible 137651.19 (148.46 seconds)\n", + "Cbc0010I After 60200 nodes, 13819 on tree, 139485.54 best solution, best possible 137651.19 (148.70 seconds)\n", + "Cbc0010I After 60300 nodes, 13798 on tree, 139485.54 best solution, best possible 137651.19 (148.87 seconds)\n", + "Cbc0010I After 60400 nodes, 13799 on tree, 139485.54 best solution, best possible 137651.19 (149.09 seconds)\n", + "Cbc0010I After 60500 nodes, 13805 on tree, 139485.54 best solution, best possible 137651.19 (149.28 seconds)\n", + "Cbc0010I After 60600 nodes, 13802 on tree, 139485.54 best solution, best possible 137651.19 (149.50 seconds)\n", + "Cbc0010I After 60700 nodes, 13815 on tree, 139485.54 best solution, best possible 137651.19 (149.73 seconds)\n", + "Cbc0010I After 60800 nodes, 13806 on tree, 139485.54 best solution, best possible 137651.19 (149.91 seconds)\n", + "Cbc0010I After 60900 nodes, 13803 on tree, 139485.54 best solution, best possible 137651.19 (150.10 seconds)\n", + "Cbc0010I After 61000 nodes, 13803 on tree, 139485.54 best solution, best possible 137651.19 (150.24 seconds)\n", + "Cbc0010I After 61100 nodes, 13809 on tree, 139485.54 best solution, best possible 137651.19 (150.47 seconds)\n", + "Cbc0010I After 61200 nodes, 13796 on tree, 139485.54 best solution, best possible 137651.19 (150.66 seconds)\n", + "Cbc0010I After 61300 nodes, 13807 on tree, 139485.54 best solution, best possible 137651.19 (150.96 seconds)\n", + "Cbc0010I After 61400 nodes, 13808 on tree, 139485.54 best solution, best possible 137651.19 (151.19 seconds)\n", + "Cbc0010I After 61500 nodes, 13811 on tree, 139485.54 best solution, best possible 137651.19 (151.47 seconds)\n", + "Cbc0010I After 61600 nodes, 13805 on tree, 139485.54 best solution, best possible 137651.19 (151.68 seconds)\n", + "Cbc0010I After 61700 nodes, 13797 on tree, 139485.54 best solution, best possible 137651.19 (151.94 seconds)\n", + "Cbc0010I After 61800 nodes, 13804 on tree, 139485.54 best solution, best possible 137651.19 (152.15 seconds)\n", + "Cbc0010I After 61900 nodes, 13799 on tree, 139485.54 best solution, best possible 137651.19 (152.43 seconds)\n", + "Cbc0010I After 62000 nodes, 13803 on tree, 139485.54 best solution, best possible 137651.19 (152.74 seconds)\n", + "Cbc0010I After 62100 nodes, 13800 on tree, 139485.54 best solution, best possible 137651.19 (153.10 seconds)\n", + "Cbc0010I After 62200 nodes, 13809 on tree, 139485.54 best solution, best possible 137651.19 (153.38 seconds)\n", + "Cbc0010I After 62300 nodes, 13810 on tree, 139485.54 best solution, best possible 137651.19 (153.59 seconds)\n", + "Cbc0010I After 62400 nodes, 13804 on tree, 139485.54 best solution, best possible 137651.19 (153.80 seconds)\n", + "Cbc0010I After 62500 nodes, 13808 on tree, 139485.54 best solution, best possible 137651.19 (154.06 seconds)\n", + "Cbc0010I After 62600 nodes, 13802 on tree, 139485.54 best solution, best possible 137651.19 (154.27 seconds)\n", + "Cbc0010I After 62700 nodes, 13799 on tree, 139485.54 best solution, best possible 137651.19 (154.47 seconds)\n", + "Cbc0010I After 62800 nodes, 13799 on tree, 139485.54 best solution, best possible 137651.19 (154.64 seconds)\n", + "Cbc0010I After 62900 nodes, 13793 on tree, 139485.54 best solution, best possible 137651.19 (154.79 seconds)\n", + "Cbc0010I After 63000 nodes, 13775 on tree, 139485.54 best solution, best possible 137651.19 (154.98 seconds)\n", + "Cbc0010I After 63100 nodes, 13823 on tree, 139485.54 best solution, best possible 137651.19 (155.37 seconds)\n", + "Cbc0010I After 63200 nodes, 13863 on tree, 139485.54 best solution, best possible 137651.19 (155.71 seconds)\n", + "Cbc0010I After 63300 nodes, 13913 on tree, 139485.54 best solution, best possible 137651.19 (156.05 seconds)\n", + "Cbc0010I After 63400 nodes, 13953 on tree, 139485.54 best solution, best possible 137651.19 (156.39 seconds)\n", + "Cbc0010I After 63500 nodes, 14004 on tree, 139485.54 best solution, best possible 137651.19 (156.68 seconds)\n", + "Cbc0010I After 63600 nodes, 14048 on tree, 139485.54 best solution, best possible 137651.19 (157.06 seconds)\n", + "Cbc0010I After 63700 nodes, 14091 on tree, 139485.54 best solution, best possible 137651.19 (157.45 seconds)\n", + "Cbc0010I After 63800 nodes, 14135 on tree, 139485.54 best solution, best possible 137651.19 (157.77 seconds)\n", + "Cbc0010I After 63900 nodes, 14179 on tree, 139485.54 best solution, best possible 137651.19 (158.11 seconds)\n", + "Cbc0010I After 64000 nodes, 14223 on tree, 139485.54 best solution, best possible 137651.19 (158.45 seconds)\n", + "Cbc0010I After 64100 nodes, 14219 on tree, 139485.54 best solution, best possible 137651.19 (158.71 seconds)\n", + "Cbc0010I After 64200 nodes, 14235 on tree, 139485.54 best solution, best possible 137651.19 (158.96 seconds)\n", + "Cbc0010I After 64300 nodes, 14220 on tree, 139485.54 best solution, best possible 137651.19 (159.15 seconds)\n", + "Cbc0010I After 64400 nodes, 14211 on tree, 139485.54 best solution, best possible 137651.19 (159.37 seconds)\n", + "Cbc0010I After 64500 nodes, 14213 on tree, 139485.54 best solution, best possible 137651.19 (159.55 seconds)\n", + "Cbc0010I After 64600 nodes, 14177 on tree, 139485.54 best solution, best possible 137651.19 (159.71 seconds)\n", + "Cbc0010I After 64700 nodes, 14152 on tree, 139485.54 best solution, best possible 137651.19 (159.92 seconds)\n", + "Cbc0010I After 64800 nodes, 14147 on tree, 139485.54 best solution, best possible 137651.19 (160.19 seconds)\n", + "Cbc0010I After 64900 nodes, 14134 on tree, 139485.54 best solution, best possible 137651.19 (160.44 seconds)\n", + "Cbc0010I After 65000 nodes, 14111 on tree, 139485.54 best solution, best possible 137651.19 (160.59 seconds)\n", + "Cbc0010I After 65100 nodes, 14099 on tree, 139485.54 best solution, best possible 137651.19 (160.77 seconds)\n", + "Cbc0010I After 65200 nodes, 14103 on tree, 139485.54 best solution, best possible 137651.19 (161.04 seconds)\n", + "Cbc0010I After 65300 nodes, 14097 on tree, 139485.54 best solution, best possible 137651.19 (161.28 seconds)\n", + "Cbc0010I After 65400 nodes, 14099 on tree, 139485.54 best solution, best possible 137651.19 (161.49 seconds)\n", + "Cbc0010I After 65500 nodes, 14104 on tree, 139485.54 best solution, best possible 137651.19 (161.75 seconds)\n", + "Cbc0010I After 65600 nodes, 14105 on tree, 139485.54 best solution, best possible 137651.19 (161.94 seconds)\n", + "Cbc0010I After 65700 nodes, 14100 on tree, 139485.54 best solution, best possible 137651.19 (162.12 seconds)\n", + "Cbc0010I After 65800 nodes, 14115 on tree, 139485.54 best solution, best possible 137651.19 (162.32 seconds)\n", + "Cbc0010I After 65900 nodes, 14105 on tree, 139485.54 best solution, best possible 6.6460908e-311 (162.48 seconds)\n", + "Cbc0010I After 66000 nodes, 14106 on tree, 139485.54 best solution, best possible 137651.19 (162.70 seconds)\n", + "Cbc0010I After 66100 nodes, 14105 on tree, 139485.54 best solution, best possible 137651.19 (162.85 seconds)\n", + "Cbc0010I After 66200 nodes, 14109 on tree, 139485.54 best solution, best possible 137651.19 (163.01 seconds)\n", + "Cbc0010I After 66300 nodes, 14103 on tree, 139485.54 best solution, best possible 137651.19 (163.27 seconds)\n", + "Cbc0010I After 66400 nodes, 14105 on tree, 139485.54 best solution, best possible 137651.19 (163.59 seconds)\n", + "Cbc0010I After 66500 nodes, 14107 on tree, 139485.54 best solution, best possible 137651.19 (163.83 seconds)\n", + "Cbc0010I After 66600 nodes, 14117 on tree, 139485.54 best solution, best possible 137651.19 (164.06 seconds)\n", + "Cbc0010I After 66700 nodes, 14113 on tree, 139485.54 best solution, best possible 137651.19 (164.35 seconds)\n", + "Cbc0010I After 66800 nodes, 14108 on tree, 139485.54 best solution, best possible 137651.19 (164.62 seconds)\n", + "Cbc0010I After 66900 nodes, 14104 on tree, 139485.54 best solution, best possible 137651.19 (164.86 seconds)\n", + "Cbc0010I After 67000 nodes, 14110 on tree, 139485.54 best solution, best possible 137651.19 (165.07 seconds)\n", + "Cbc0010I After 67100 nodes, 14104 on tree, 139485.54 best solution, best possible 137651.19 (165.32 seconds)\n", + "Cbc0010I After 67200 nodes, 14101 on tree, 139485.54 best solution, best possible 137651.19 (165.65 seconds)\n", + "Cbc0010I After 67300 nodes, 14109 on tree, 139485.54 best solution, best possible 137651.19 (165.92 seconds)\n", + "Cbc0010I After 67400 nodes, 14106 on tree, 139485.54 best solution, best possible 137651.19 (166.17 seconds)\n", + "Cbc0010I After 67500 nodes, 14109 on tree, 139485.54 best solution, best possible 137651.19 (166.44 seconds)\n", + "Cbc0010I After 67600 nodes, 14104 on tree, 139485.54 best solution, best possible 137651.19 (166.68 seconds)\n", + "Cbc0010I After 67700 nodes, 14112 on tree, 139485.54 best solution, best possible 137651.19 (166.86 seconds)\n", + "Cbc0010I After 67800 nodes, 14099 on tree, 139485.54 best solution, best possible 137651.19 (167.00 seconds)\n", + "Cbc0010I After 67900 nodes, 14050 on tree, 139485.54 best solution, best possible 137651.19 (167.22 seconds)\n", + "Cbc0010I After 68000 nodes, 14025 on tree, 139485.54 best solution, best possible 137651.19 (167.50 seconds)\n", + "Cbc0010I After 68100 nodes, 14046 on tree, 139485.54 best solution, best possible 137651.19 (167.71 seconds)\n", + "Cbc0010I After 68200 nodes, 14017 on tree, 139485.54 best solution, best possible 137651.19 (167.90 seconds)\n", + "Cbc0010I After 68300 nodes, 14007 on tree, 139485.54 best solution, best possible 137651.19 (168.12 seconds)\n", + "Cbc0010I After 68400 nodes, 14039 on tree, 139485.54 best solution, best possible 137651.19 (168.40 seconds)\n", + "Cbc0010I After 68500 nodes, 14042 on tree, 139485.54 best solution, best possible 137651.19 (168.64 seconds)\n", + "Cbc0010I After 68600 nodes, 14029 on tree, 139485.54 best solution, best possible 137651.19 (168.83 seconds)\n", + "Cbc0010I After 68700 nodes, 14024 on tree, 139485.54 best solution, best possible 137651.19 (169.02 seconds)\n", + "Cbc0010I After 68800 nodes, 14015 on tree, 139485.54 best solution, best possible 137651.19 (169.21 seconds)\n", + "Cbc0010I After 68900 nodes, 14006 on tree, 139485.54 best solution, best possible 137651.19 (169.38 seconds)\n", + "Cbc0010I After 69000 nodes, 13964 on tree, 139485.54 best solution, best possible 137651.19 (169.61 seconds)\n", + "Cbc0010I After 69100 nodes, 13957 on tree, 139485.54 best solution, best possible 137651.19 (169.88 seconds)\n", + "Cbc0010I After 69200 nodes, 13941 on tree, 139485.54 best solution, best possible 137651.19 (170.10 seconds)\n", + "Cbc0010I After 69300 nodes, 13957 on tree, 139485.54 best solution, best possible 137651.19 (170.34 seconds)\n", + "Cbc0010I After 69400 nodes, 13942 on tree, 139485.54 best solution, best possible 137651.19 (170.56 seconds)\n", + "Cbc0010I After 69500 nodes, 13972 on tree, 139485.54 best solution, best possible 137651.19 (170.76 seconds)\n", + "Cbc0010I After 69600 nodes, 13955 on tree, 139485.54 best solution, best possible 137651.19 (170.93 seconds)\n", + "Cbc0010I After 69700 nodes, 13947 on tree, 139485.54 best solution, best possible 137651.19 (171.11 seconds)\n", + "Cbc0010I After 69800 nodes, 13944 on tree, 139485.54 best solution, best possible 137651.19 (171.30 seconds)\n", + "Cbc0010I After 69900 nodes, 13948 on tree, 139485.54 best solution, best possible 137651.19 (171.56 seconds)\n", + "Cbc0010I After 70000 nodes, 13946 on tree, 139485.54 best solution, best possible 137651.19 (171.77 seconds)\n", + "Cbc0010I After 70100 nodes, 13961 on tree, 139485.54 best solution, best possible 137651.19 (172.09 seconds)\n", + "Cbc0010I After 70200 nodes, 13945 on tree, 139485.54 best solution, best possible 137651.19 (172.38 seconds)\n", + "Cbc0010I After 70300 nodes, 13950 on tree, 139485.54 best solution, best possible 137651.19 (172.63 seconds)\n", + "Cbc0010I After 70400 nodes, 13963 on tree, 139485.54 best solution, best possible 137651.19 (172.97 seconds)\n", + "Cbc0010I After 70500 nodes, 13965 on tree, 139485.54 best solution, best possible 137651.19 (173.24 seconds)\n", + "Cbc0010I After 70600 nodes, 13954 on tree, 139485.54 best solution, best possible 137651.19 (173.49 seconds)\n", + "Cbc0010I After 70700 nodes, 13956 on tree, 139485.54 best solution, best possible 137651.19 (173.75 seconds)\n", + "Cbc0012I Integer solution of 139479.9 found by heuristic after 5063076 iterations and 70766 nodes (173.93 seconds)\n", + "Cbc0010I After 70800 nodes, 13949 on tree, 139479.9 best solution, best possible 137651.19 (174.06 seconds)\n", + "Cbc0010I After 70900 nodes, 13992 on tree, 139479.9 best solution, best possible 137651.19 (174.45 seconds)\n", + "Cbc0010I After 71000 nodes, 14036 on tree, 139479.9 best solution, best possible 137651.19 (174.80 seconds)\n", + "Cbc0010I After 71100 nodes, 14019 on tree, 139479.9 best solution, best possible 137651.19 (174.95 seconds)\n", + "Cbc0010I After 71200 nodes, 14040 on tree, 139479.9 best solution, best possible 137651.19 (175.22 seconds)\n", + "Cbc0010I After 71300 nodes, 14023 on tree, 139479.9 best solution, best possible 137651.19 (175.49 seconds)\n", + "Cbc0010I After 71400 nodes, 14026 on tree, 139479.9 best solution, best possible 137651.19 (175.75 seconds)\n", + "Cbc0010I After 71500 nodes, 14062 on tree, 139479.9 best solution, best possible 137651.19 (176.17 seconds)\n", + "Cbc0010I After 71600 nodes, 14055 on tree, 139479.9 best solution, best possible 137651.19 (176.45 seconds)\n", + "Cbc0010I After 71700 nodes, 14039 on tree, 139479.9 best solution, best possible 137651.19 (176.80 seconds)\n", + "Cbc0010I After 71800 nodes, 14029 on tree, 139479.9 best solution, best possible 137651.19 (177.08 seconds)\n", + "Cbc0010I After 71900 nodes, 14032 on tree, 139479.9 best solution, best possible 137651.19 (177.44 seconds)\n", + "Cbc0010I After 72000 nodes, 14020 on tree, 139479.9 best solution, best possible 137651.19 (177.77 seconds)\n", + "Cbc0010I After 72100 nodes, 14018 on tree, 139479.9 best solution, best possible 137651.19 (178.07 seconds)\n", + "Cbc0010I After 72200 nodes, 14021 on tree, 139479.9 best solution, best possible 137651.19 (178.31 seconds)\n", + "Cbc0010I After 72300 nodes, 14022 on tree, 139479.9 best solution, best possible 137651.19 (178.55 seconds)\n", + "Cbc0010I After 72400 nodes, 14019 on tree, 139479.9 best solution, best possible 137651.19 (178.76 seconds)\n", + "Cbc0010I After 72500 nodes, 13991 on tree, 139479.9 best solution, best possible 137651.19 (178.95 seconds)\n", + "Cbc0010I After 72600 nodes, 13970 on tree, 139479.9 best solution, best possible 137651.19 (179.15 seconds)\n", + "Cbc0010I After 72700 nodes, 13944 on tree, 139479.9 best solution, best possible 137651.19 (179.37 seconds)\n", + "Cbc0010I After 72800 nodes, 13945 on tree, 139479.9 best solution, best possible 137651.19 (179.63 seconds)\n", + "Cbc0010I After 72900 nodes, 13927 on tree, 139479.9 best solution, best possible 137651.19 (179.91 seconds)\n", + "Cbc0010I After 73000 nodes, 13898 on tree, 139479.9 best solution, best possible 137651.19 (180.19 seconds)\n", + "Cbc0010I After 73100 nodes, 13907 on tree, 139479.9 best solution, best possible 137651.19 (180.45 seconds)\n", + "Cbc0010I After 73200 nodes, 13884 on tree, 139479.9 best solution, best possible 137651.19 (180.72 seconds)\n", + "Cbc0010I After 73300 nodes, 13852 on tree, 139479.9 best solution, best possible 137651.19 (180.98 seconds)\n", + "Cbc0010I After 73400 nodes, 13839 on tree, 139479.9 best solution, best possible 137651.19 (181.20 seconds)\n", + "Cbc0010I After 73500 nodes, 13838 on tree, 139479.9 best solution, best possible 137651.19 (181.38 seconds)\n", + "Cbc0010I After 73600 nodes, 13841 on tree, 139479.9 best solution, best possible 137651.19 (181.61 seconds)\n", + "Cbc0010I After 73700 nodes, 13857 on tree, 139479.9 best solution, best possible 137651.19 (181.84 seconds)\n", + "Cbc0010I After 73800 nodes, 13849 on tree, 139479.9 best solution, best possible 137651.19 (182.07 seconds)\n", + "Cbc0010I After 73900 nodes, 13848 on tree, 139479.9 best solution, best possible 137651.19 (182.32 seconds)\n", + "Cbc0010I After 74000 nodes, 13845 on tree, 139479.9 best solution, best possible 137651.19 (182.53 seconds)\n", + "Cbc0010I After 74100 nodes, 13841 on tree, 139479.9 best solution, best possible 137651.19 (182.80 seconds)\n", + "Cbc0010I After 74200 nodes, 13848 on tree, 139479.9 best solution, best possible 137651.19 (183.09 seconds)\n", + "Cbc0010I After 74300 nodes, 13837 on tree, 139479.9 best solution, best possible 137651.19 (183.26 seconds)\n", + "Cbc0010I After 74400 nodes, 13841 on tree, 139479.9 best solution, best possible 137651.19 (183.49 seconds)\n", + "Cbc0010I After 74500 nodes, 13850 on tree, 139479.9 best solution, best possible 137651.19 (183.75 seconds)\n", + "Cbc0010I After 74600 nodes, 13837 on tree, 139479.9 best solution, best possible 137651.19 (184.00 seconds)\n", + "Cbc0010I After 74700 nodes, 13842 on tree, 139479.9 best solution, best possible 137651.19 (184.27 seconds)\n", + "Cbc0010I After 74800 nodes, 13844 on tree, 139479.9 best solution, best possible 137651.19 (184.53 seconds)\n", + "Cbc0010I After 74900 nodes, 13844 on tree, 139479.9 best solution, best possible 137651.19 (184.80 seconds)\n", + "Cbc0010I After 75000 nodes, 13850 on tree, 139479.9 best solution, best possible 137651.19 (185.09 seconds)\n", + "Cbc0010I After 75100 nodes, 13841 on tree, 139479.9 best solution, best possible 137651.19 (185.30 seconds)\n", + "Cbc0010I After 75200 nodes, 13863 on tree, 139479.9 best solution, best possible 137651.19 (185.57 seconds)\n", + "Cbc0010I After 75300 nodes, 13850 on tree, 139479.9 best solution, best possible 137651.19 (185.82 seconds)\n", + "Cbc0010I After 75400 nodes, 13851 on tree, 139479.9 best solution, best possible 137651.19 (186.08 seconds)\n", + "Cbc0010I After 75500 nodes, 13841 on tree, 139479.9 best solution, best possible 137651.19 (186.42 seconds)\n", + "Cbc0010I After 75600 nodes, 13846 on tree, 139479.9 best solution, best possible 137651.19 (186.81 seconds)\n", + "Cbc0010I After 75700 nodes, 13842 on tree, 139479.9 best solution, best possible 137651.19 (187.15 seconds)\n", + "Cbc0010I After 75800 nodes, 13846 on tree, 139479.9 best solution, best possible 137651.19 (187.38 seconds)\n", + "Cbc0010I After 75900 nodes, 13850 on tree, 139479.9 best solution, best possible 137651.19 (187.58 seconds)\n", + "Cbc0010I After 76000 nodes, 13854 on tree, 139479.9 best solution, best possible 137651.19 (187.71 seconds)\n", + "Cbc0010I After 76100 nodes, 13842 on tree, 139479.9 best solution, best possible 137651.19 (187.86 seconds)\n", + "Cbc0010I After 76200 nodes, 13843 on tree, 139479.9 best solution, best possible 137651.19 (188.13 seconds)\n", + "Cbc0010I After 76300 nodes, 13843 on tree, 139479.9 best solution, best possible 137651.19 (188.46 seconds)\n", + "Cbc0010I After 76400 nodes, 13844 on tree, 139479.9 best solution, best possible 137651.19 (188.70 seconds)\n", + "Cbc0010I After 76500 nodes, 13829 on tree, 139479.9 best solution, best possible 137651.19 (188.91 seconds)\n", + "Cbc0010I After 76600 nodes, 13814 on tree, 139479.9 best solution, best possible 137651.19 (189.19 seconds)\n", + "Cbc0010I After 76700 nodes, 13807 on tree, 139479.9 best solution, best possible 137651.19 (189.45 seconds)\n", + "Cbc0010I After 76800 nodes, 13767 on tree, 139479.9 best solution, best possible 137651.19 (189.69 seconds)\n", + "Cbc0010I After 76900 nodes, 13764 on tree, 139479.9 best solution, best possible 137651.19 (189.96 seconds)\n", + "Cbc0010I After 77000 nodes, 13758 on tree, 139479.9 best solution, best possible 137651.19 (190.34 seconds)\n", + "Cbc0010I After 77100 nodes, 13806 on tree, 139479.9 best solution, best possible 137652.67 (190.85 seconds)\n", + "Cbc0010I After 77200 nodes, 13852 on tree, 139479.9 best solution, best possible 137654.17 (191.30 seconds)\n", + "Cbc0010I After 77300 nodes, 13902 on tree, 139479.9 best solution, best possible 137655.99 (191.77 seconds)\n", + "Cbc0010I After 77400 nodes, 13947 on tree, 139479.9 best solution, best possible 137658.22 (192.20 seconds)\n", + "Cbc0010I After 77500 nodes, 13995 on tree, 139479.9 best solution, best possible 137659.96 (192.57 seconds)\n", + "Cbc0010I After 77600 nodes, 14043 on tree, 139479.9 best solution, best possible 137661.49 (192.91 seconds)\n", + "Cbc0010I After 77700 nodes, 14093 on tree, 139479.9 best solution, best possible 137662.72 (193.30 seconds)\n", + "Cbc0010I After 77800 nodes, 14140 on tree, 139479.9 best solution, best possible 137665.1 (193.72 seconds)\n", + "Cbc0010I After 77900 nodes, 14190 on tree, 139479.9 best solution, best possible 137666.98 (194.13 seconds)\n", + "Cbc0010I After 78000 nodes, 14239 on tree, 139479.9 best solution, best possible 137668.78 (194.50 seconds)\n", + "Cbc0010I After 78100 nodes, 14224 on tree, 139479.9 best solution, best possible 137669.11 (194.75 seconds)\n", + "Cbc0010I After 78200 nodes, 14215 on tree, 139479.9 best solution, best possible 137669.11 (194.98 seconds)\n", + "Cbc0010I After 78300 nodes, 14192 on tree, 139479.9 best solution, best possible 137669.11 (195.15 seconds)\n", + "Cbc0010I After 78400 nodes, 14201 on tree, 139479.9 best solution, best possible 137669.11 (195.37 seconds)\n", + "Cbc0010I After 78500 nodes, 14192 on tree, 139479.9 best solution, best possible 137669.11 (195.58 seconds)\n", + "Cbc0010I After 78600 nodes, 14188 on tree, 139479.9 best solution, best possible 137669.11 (195.77 seconds)\n", + "Cbc0010I After 78700 nodes, 14193 on tree, 139479.9 best solution, best possible 137669.11 (195.95 seconds)\n", + "Cbc0010I After 78800 nodes, 14188 on tree, 139479.9 best solution, best possible 137669.11 (196.12 seconds)\n", + "Cbc0010I After 78900 nodes, 14187 on tree, 139479.9 best solution, best possible 137669.11 (196.32 seconds)\n", + "Cbc0010I After 79000 nodes, 14190 on tree, 139479.9 best solution, best possible 137669.11 (196.54 seconds)\n", + "Cbc0010I After 79100 nodes, 14202 on tree, 139479.9 best solution, best possible 137669.11 (196.77 seconds)\n", + "Cbc0010I After 79200 nodes, 14194 on tree, 139479.9 best solution, best possible 137669.11 (197.00 seconds)\n", + "Cbc0010I After 79300 nodes, 14193 on tree, 139479.9 best solution, best possible 137669.11 (197.24 seconds)\n", + "Cbc0010I After 79400 nodes, 14189 on tree, 139479.9 best solution, best possible 6.6460908e-311 (197.46 seconds)\n", + "Cbc0010I After 79500 nodes, 14186 on tree, 139479.9 best solution, best possible 137669.11 (197.73 seconds)\n", + "Cbc0010I After 79600 nodes, 14186 on tree, 139479.9 best solution, best possible 137669.11 (198.00 seconds)\n", + "Cbc0010I After 79700 nodes, 14184 on tree, 139479.9 best solution, best possible 137669.11 (198.31 seconds)\n", + "Cbc0010I After 79800 nodes, 14198 on tree, 139479.9 best solution, best possible 137669.11 (198.60 seconds)\n", + "Cbc0010I After 79900 nodes, 14195 on tree, 139479.9 best solution, best possible 137669.11 (198.84 seconds)\n", + "Cbc0010I After 80000 nodes, 14195 on tree, 139479.9 best solution, best possible 137669.11 (199.08 seconds)\n", + "Cbc0010I After 80100 nodes, 14200 on tree, 139479.9 best solution, best possible 137669.11 (199.28 seconds)\n", + "Cbc0010I After 80200 nodes, 14199 on tree, 139479.9 best solution, best possible 137669.11 (199.54 seconds)\n", + "Cbc0010I After 80300 nodes, 14204 on tree, 139479.9 best solution, best possible 137669.11 (199.81 seconds)\n", + "Cbc0010I After 80400 nodes, 14188 on tree, 139479.9 best solution, best possible 137669.11 (200.04 seconds)\n", + "Cbc0010I After 80500 nodes, 14201 on tree, 139479.9 best solution, best possible 137669.11 (200.30 seconds)\n", + "Cbc0010I After 80600 nodes, 14201 on tree, 139479.9 best solution, best possible 137669.11 (200.49 seconds)\n", + "Cbc0010I After 80700 nodes, 14187 on tree, 139479.9 best solution, best possible 137669.11 (200.73 seconds)\n", + "Cbc0010I After 80800 nodes, 14200 on tree, 139479.9 best solution, best possible 137669.11 (200.98 seconds)\n", + "Cbc0010I After 80900 nodes, 14194 on tree, 139479.9 best solution, best possible 137669.11 (201.21 seconds)\n", + "Cbc0010I After 81000 nodes, 14184 on tree, 139479.9 best solution, best possible 137669.11 (201.42 seconds)\n", + "Cbc0010I After 81100 nodes, 14189 on tree, 139479.9 best solution, best possible 137669.11 (201.64 seconds)\n", + "Cbc0010I After 81200 nodes, 14188 on tree, 139479.9 best solution, best possible 137669.11 (201.97 seconds)\n", + "Cbc0010I After 81300 nodes, 14181 on tree, 139479.9 best solution, best possible 137669.11 (202.27 seconds)\n", + "Cbc0010I After 81400 nodes, 14191 on tree, 139479.9 best solution, best possible 137669.11 (202.64 seconds)\n", + "Cbc0010I After 81500 nodes, 14187 on tree, 139479.9 best solution, best possible 137669.11 (202.90 seconds)\n", + "Cbc0010I After 81600 nodes, 14194 on tree, 139479.9 best solution, best possible 137669.11 (203.17 seconds)\n", + "Cbc0010I After 81700 nodes, 14194 on tree, 139479.9 best solution, best possible 137669.11 (203.39 seconds)\n", + "Cbc0010I After 81800 nodes, 14194 on tree, 139479.9 best solution, best possible 137669.11 (203.71 seconds)\n", + "Cbc0010I After 81900 nodes, 14189 on tree, 139479.9 best solution, best possible 137669.11 (203.98 seconds)\n", + "Cbc0010I After 82000 nodes, 14192 on tree, 139479.9 best solution, best possible 137669.11 (204.25 seconds)\n", + "Cbc0010I After 82100 nodes, 14203 on tree, 139479.9 best solution, best possible 137669.11 (204.52 seconds)\n", + "Cbc0010I After 82200 nodes, 14186 on tree, 139479.9 best solution, best possible 137669.11 (204.75 seconds)\n", + "Cbc0010I After 82300 nodes, 14184 on tree, 139479.9 best solution, best possible 137669.11 (204.93 seconds)\n", + "Cbc0010I After 82400 nodes, 14177 on tree, 139479.9 best solution, best possible 137669.11 (205.09 seconds)\n", + "Cbc0010I After 82500 nodes, 14160 on tree, 139479.9 best solution, best possible 137669.11 (205.26 seconds)\n", + "Cbc0010I After 82600 nodes, 14147 on tree, 139479.9 best solution, best possible 137669.11 (205.46 seconds)\n", + "Cbc0010I After 82700 nodes, 14125 on tree, 139479.9 best solution, best possible 137669.11 (205.70 seconds)\n", + "Cbc0010I After 82800 nodes, 14102 on tree, 139479.9 best solution, best possible 137669.11 (205.96 seconds)\n", + "Cbc0010I After 82900 nodes, 14109 on tree, 139479.9 best solution, best possible 137669.11 (206.26 seconds)\n", + "Cbc0010I After 83000 nodes, 14097 on tree, 139479.9 best solution, best possible 137669.11 (206.45 seconds)\n", + "Cbc0010I After 83100 nodes, 14089 on tree, 139479.9 best solution, best possible 137669.11 (206.66 seconds)\n", + "Cbc0010I After 83200 nodes, 14064 on tree, 139479.9 best solution, best possible 137669.11 (206.86 seconds)\n", + "Cbc0010I After 83300 nodes, 14054 on tree, 139479.9 best solution, best possible 137669.11 (207.11 seconds)\n", + "Cbc0010I After 83400 nodes, 14059 on tree, 139479.9 best solution, best possible 137669.11 (207.37 seconds)\n", + "Cbc0010I After 83500 nodes, 14047 on tree, 139479.9 best solution, best possible 137669.11 (207.66 seconds)\n", + "Cbc0010I After 83600 nodes, 14047 on tree, 139479.9 best solution, best possible 137669.11 (207.90 seconds)\n", + "Cbc0010I After 83700 nodes, 14049 on tree, 139479.9 best solution, best possible 137669.11 (208.16 seconds)\n", + "Cbc0010I After 83800 nodes, 14042 on tree, 139479.9 best solution, best possible 137669.11 (208.35 seconds)\n", + "Cbc0010I After 83900 nodes, 14046 on tree, 139479.9 best solution, best possible 137669.11 (208.51 seconds)\n", + "Cbc0010I After 84000 nodes, 14053 on tree, 139479.9 best solution, best possible 137669.11 (208.71 seconds)\n", + "Cbc0010I After 84100 nodes, 14055 on tree, 139479.9 best solution, best possible 137669.11 (208.91 seconds)\n", + "Cbc0010I After 84200 nodes, 14051 on tree, 139479.9 best solution, best possible 137669.11 (209.06 seconds)\n", + "Cbc0010I After 84300 nodes, 14045 on tree, 139479.9 best solution, best possible 137669.11 (209.27 seconds)\n", + "Cbc0010I After 84400 nodes, 14054 on tree, 139479.9 best solution, best possible 137669.11 (209.54 seconds)\n", + "Cbc0010I After 84500 nodes, 14046 on tree, 139479.9 best solution, best possible 137669.11 (209.82 seconds)\n", + "Cbc0010I After 84600 nodes, 14057 on tree, 139479.9 best solution, best possible 137669.11 (210.10 seconds)\n", + "Cbc0030I Thread 0 used 10308 times, waiting to start 1.5960991, 58400 locks, 4.0256364 locked, 1.1038821 waiting for locks\n", + "Cbc0030I Thread 1 used 10438 times, waiting to start 1.6692264, 59198 locks, 4.1803491 locked, 1.2286246 waiting for locks\n", + "Cbc0030I Thread 2 used 10933 times, waiting to start 2.0395281, 61562 locks, 4.3029604 locked, 1.2401576 waiting for locks\n", + "Cbc0030I Thread 3 used 10034 times, waiting to start 1.9317975, 56757 locks, 4.0053208 locked, 1.1157246 waiting for locks\n", + "Cbc0030I Thread 4 used 11039 times, waiting to start 2.1977839, 62134 locks, 4.3901339 locked, 1.2734587 waiting for locks\n", + "Cbc0030I Thread 5 used 10829 times, waiting to start 2.1406515, 60996 locks, 4.3205426 locked, 1.3111045 waiting for locks\n", + "Cbc0030I Thread 6 used 10417 times, waiting to start 2.1943431, 58835 locks, 4.18641 locked, 1.2421923 waiting for locks\n", + "Cbc0030I Thread 7 used 10640 times, waiting to start 2.1942604, 60157 locks, 4.2023678 locked, 1.2935784 waiting for locks\n", + "Cbc0030I Main thread 196.70116 waiting for threads, 170235 locks, 0.27426338 locked, 2.3985751 waiting for locks\n", + "Cbc0027I Exiting on user event\n", + "Cbc0005I Partial search - best objective 139479.9 (best possible 137669.11), took 5901854 iterations and 84631 nodes (211.18 seconds)\n", + "Cbc0032I Strong branching done 98944 times (3527470 iterations), fathomed 3676 nodes and fixed 13898 variables\n", + "Cbc0035I Maximum depth 79, 2008157 variables fixed on reduced cost\n", + "Cuts at root node changed objective from 135593 to 136487\n", + "Probing was tried 99 times and created 27 cuts of which 0 were active after adding rounds of cuts (0.075 seconds)\n", + "Gomory was tried 26437 times and created 15842 cuts of which 0 were active after adding rounds of cuts (83.348 seconds)\n", + "Knapsack was tried 99 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.021 seconds)\n", + "Clique was tried 99 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.004 seconds)\n", + "MixedIntegerRounding2 was tried 99 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.028 seconds)\n", + "FlowCover was tried 26445 times and created 169051 cuts of which 0 were active after adding rounds of cuts (34.785 seconds)\n", + "TwoMirCuts was tried 26437 times and created 18936 cuts of which 0 were active after adding rounds of cuts (48.291 seconds)\n", + "ZeroHalf was tried 99 times and created 117 cuts of which 0 were active after adding rounds of cuts (0.103 seconds)\n", + "\n", + "Result - User ctrl-cuser ctrl-c\n", + "\n", + "Objective value: 139479.89521600\n", + "Lower bound: 137669.114\n", + "Gap: 0.01\n", + "Enumerated nodes: 84631\n", + "Total iterations: 5901854\n", + "Time (CPU seconds): 211.25\n", + "Time (Wallclock seconds): 211.25\n", + "\n", + "Total time (CPU seconds): 211.29 (Wallclock seconds): 211.29\n", + "\n", + "WARNING: Loading a SolverResults object with an 'aborted' status, but\n", + "containing a solution\n" + ] + } + ], + "source": [ + "cost, state = wfn.design()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "47048032-60a0-488f-98e0-87b6a98e3451", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:30:10.447218Z", + "iopub.status.busy": "2024-02-06T13:30:10.447218Z", + "iopub.status.idle": "2024-02-06T13:30:10.847272Z", + "shell.execute_reply": "2024-02-06T13:30:10.847272Z", + "shell.execute_reply.started": "2024-02-06T13:30:10.447218Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "<Axes: >" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "wfn.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ac8ac89d-af01-4019-a0b7-2e054273587e", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:30:23.930507Z", + "iopub.status.busy": "2024-02-06T13:30:23.930507Z", + "iopub.status.idle": "2024-02-06T13:30:23.942507Z", + "shell.execute_reply": "2024-02-06T13:30:23.942507Z", + "shell.execute_reply.started": "2024-02-06T13:30:23.930507Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total cable length: 139480 m\n" + ] + } + ], + "source": [ + "print(f'Total cable length: {round(wfn.tree_as_table().cable_length.sum())} m')" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "d4892305-d1ae-422c-8243-484c74616a45", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:30:57.141237Z", + "iopub.status.busy": "2024-02-06T13:30:57.141237Z", + "iopub.status.idle": "2024-02-06T13:30:57.160019Z", + "shell.execute_reply": "2024-02-06T13:30:57.159024Z", + "shell.execute_reply.started": "2024-02-06T13:30:57.141237Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total cost: 40586828 €\n" + ] + } + ], + "source": [ + "print(f'Total cost: {round(cost)} €')" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "4c96c58c-c918-447f-a8b8-80ac98a42af3", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:30:59.762764Z", + "iopub.status.busy": "2024-02-06T13:30:59.762764Z", + "iopub.status.idle": "2024-02-06T13:30:59.786766Z", + "shell.execute_reply": "2024-02-06T13:30:59.785761Z", + "shell.execute_reply.started": "2024-02-06T13:30:59.762764Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>from_node</th>\n", + " <th>to_node</th>\n", + " <th>cable_length</th>\n", + " <th>cable_type</th>\n", + " <th>cable_cost</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>2</td>\n", + " <td>4</td>\n", + " <td>1993.859321</td>\n", + " <td>0</td>\n", + " <td>410735.020080</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>3</td>\n", + " <td>6</td>\n", + " <td>1693.408095</td>\n", + " <td>0</td>\n", + " <td>348842.067529</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>4</td>\n", + " <td>8</td>\n", + " <td>1993.859321</td>\n", + " <td>0</td>\n", + " <td>410735.020081</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>5</td>\n", + " <td>10</td>\n", + " <td>1693.408788</td>\n", + " <td>0</td>\n", + " <td>348842.210289</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>6</td>\n", + " <td>7</td>\n", + " <td>1699.057183</td>\n", + " <td>0</td>\n", + " <td>350005.779789</td>\n", + " </tr>\n", + " <tr>\n", + " <th>...</th>\n", + " <td>...</td>\n", + " <td>...</td>\n", + " <td>...</td>\n", + " <td>...</td>\n", + " <td>...</td>\n", + " </tr>\n", + " <tr>\n", + " <th>69</th>\n", + " <td>68</td>\n", + " <td>73</td>\n", + " <td>1693.408849</td>\n", + " <td>0</td>\n", + " <td>348842.222966</td>\n", + " </tr>\n", + " <tr>\n", + " <th>70</th>\n", + " <td>68</td>\n", + " <td>75</td>\n", + " <td>1693.408716</td>\n", + " <td>0</td>\n", + " <td>348842.195437</td>\n", + " </tr>\n", + " <tr>\n", + " <th>71</th>\n", + " <td>69</td>\n", + " <td>70</td>\n", + " <td>1699.057255</td>\n", + " <td>1</td>\n", + " <td>487629.432247</td>\n", + " </tr>\n", + " <tr>\n", + " <th>72</th>\n", + " <td>70</td>\n", + " <td>71</td>\n", + " <td>1699.057231</td>\n", + " <td>1</td>\n", + " <td>487629.425425</td>\n", + " </tr>\n", + " <tr>\n", + " <th>73</th>\n", + " <td>73</td>\n", + " <td>74</td>\n", + " <td>1699.056559</td>\n", + " <td>0</td>\n", + " <td>350005.651135</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "<p>74 rows × 5 columns</p>\n", + "</div>" + ], + "text/plain": [ + " from_node to_node cable_length cable_type cable_cost\n", + "0 2 4 1993.859321 0 410735.020080\n", + "1 3 6 1693.408095 0 348842.067529\n", + "2 4 8 1993.859321 0 410735.020081\n", + "3 5 10 1693.408788 0 348842.210289\n", + "4 6 7 1699.057183 0 350005.779789\n", + ".. ... ... ... ... ...\n", + "69 68 73 1693.408849 0 348842.222966\n", + "70 68 75 1693.408716 0 348842.195437\n", + "71 69 70 1699.057255 1 487629.432247\n", + "72 70 71 1699.057231 1 487629.425425\n", + "73 73 74 1699.056559 0 350005.651135\n", + "\n", + "[74 rows x 5 columns]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "wfn.tree_as_table()" + ] + }, + { + "cell_type": "markdown", + "id": "351c8257-0bf2-47a8-96bb-f8e7efd124ba", + "metadata": {}, + "source": [ + "## Irregular Layout" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "31b5be8c-5c17-47f6-84c3-5e5dfe74491e", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:32:24.066124Z", + "iopub.status.busy": "2024-02-06T13:32:24.065185Z", + "iopub.status.idle": "2024-02-06T13:32:24.119300Z", + "shell.execute_reply": "2024-02-06T13:32:24.118300Z", + "shell.execute_reply.started": "2024-02-06T13:32:24.066124Z" + }, + "scrolled": true + }, + "outputs": [], + "source": [ + "# Load Irregular layout from YAML file\n", + "irregular_system = load_yaml('IEA37_Borssele/IEA37_Borssele_Irregular_System.yaml')\n", + "\n", + "turbine_pos = np.array([\n", + " irregular_system['wind_farm']['layouts']['initial_layout']['coordinates']['x'],\n", + " irregular_system['wind_farm']['layouts']['initial_layout']['coordinates']['y']\n", + "])\n", + "substation_pos = np.array([\n", + " irregular_system['wind_farm']['electrical_substations']['coordinates']['x'],\n", + " irregular_system['wind_farm']['electrical_substations']['coordinates']['y']\n", + "])\n", + "\n", + "main_parcel = np.array([irregular_system['site']['boundaries']['polygons'][0]['x'],\n", + " irregular_system['site']['boundaries']['polygons'][0]['y']])\n", + "\n", + "site_info = {'site_name': irregular_system['name'],\n", + " 'handle': 'iea37irreg',\n", + " 'boundary': main_parcel.T}" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "ccdf5efa-0c6e-452e-a1f2-071eb9ab79b7", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:32:27.749540Z", + "iopub.status.busy": "2024-02-06T13:32:27.749540Z", + "iopub.status.idle": "2024-02-06T13:32:27.769538Z", + "shell.execute_reply": "2024-02-06T13:32:27.768536Z", + "shell.execute_reply.started": "2024-02-06T13:32:27.749540Z" + } + }, + "outputs": [], + "source": [ + "wfn = WindFarmNetwork(turbine_positions=turbine_pos,\n", + " substation_positions=substation_pos,\n", + " drivers=[InterArrayDriver(**interarray_setting)],\n", + " sequence=[0],\n", + " cables=cables,\n", + " site_info=site_info)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "8ea63e17-a575-43f3-ba32-0e67833d890c", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:32:30.974361Z", + "iopub.status.busy": "2024-02-06T13:32:30.974361Z", + "iopub.status.idle": "2024-02-06T13:34:15.980057Z", + "shell.execute_reply": "2024-02-06T13:34:15.979057Z", + "shell.execute_reply.started": "2024-02-06T13:32:30.974361Z" + }, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "<edge_crossing> discarding ('bn', 'Z'): would cross [('M', 'bo')]\n", + "Solving \"IEA Wind Task 37 Borssele Reference Offshore Wind Plant system (irregular layout)\"\n", + "\n", + "Welcome to the CBC MILP Solver \n", + "Version: 2.10.8 \n", + "Build Date: Jan 1 1970 \n", + "\n", + "command line - C:\\Users\\s213184\\programs\\cbc_julia_mingw32_2.10.8\\bin\\cbc.exe -ratioGap 0.002 -seconds 7200 -timeMode elapsed -threads 8 -printingOptions all -import C:\\Users\\s213184\\AppData\\Local\\Temp\\tmpc63rqlzm.pyomo.lp -mipstart \\Users\\s213184\\AppData\\Local\\Temp\\tmpehee9zfb.cbc.soln -stat=1 -solve -solu C:\\Users\\s213184\\AppData\\Local\\Temp\\tmpc63rqlzm.pyomo.soln (default strategy 1)\n", + "ratioGap was changed from 0 to 0.002\n", + "seconds was changed from 1e+100 to 7200\n", + "Option for timeMode changed from cpu to elapsed\n", + "threads was changed from 0 to 8\n", + "Option for printingOptions changed from normal to all\n", + "opening mipstart file \\Users\\s213184\\AppData\\Local\\Temp\\tmpehee9zfb.cbc.soln.\n", + "MIPStart values read for 148 variables.\n", + "Presolve 2665 (-84) rows, 1135 (-9) columns and 8453 (-619) elements\n", + "Statistics for presolved model\n", + "Original problem has 1144 integers (572 of which binary)\n", + "Presolved problem has 1135 integers (564 of which binary)\n", + "==== 571 zero objective 344 different\n", + "==== absolute objective values 344 different\n", + "==== for integers 571 zero objective 344 different\n", + "==== for integers absolute objective values 344 different\n", + "===== end objective counts\n", + "\n", + "\n", + "Problem has 2665 rows, 1135 columns (564 with objective) and 8453 elements\n", + "Column breakdown:\n", + "0 of type 0.0->inf, 571 of type 0.0->up, 0 of type lo->inf, \n", + "0 of type lo->up, 0 of type free, 0 of type fixed, \n", + "0 of type -inf->0.0, 0 of type -inf->up, 564 of type 0.0->1.0 \n", + "Row breakdown:\n", + "0 of type E 0.0, 138 of type E 1.0, 0 of type E -1.0, \n", + "2 of type E other, 0 of type G 0.0, 0 of type G 1.0, \n", + "1 of type G other, 1134 of type L 0.0, 1299 of type L 1.0, \n", + "91 of type L other, 0 of type Range 0.0->1.0, 0 of type Range other, \n", + "0 of type Free \n", + "Continuous objective value is 124045 - 0.01 seconds\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 890 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 672 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 277 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 98 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 39 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 20 strengthened rows, 0 substitutions\n", + "Cgl0003I 0 fixed, 0 tightened bounds, 3 strengthened rows, 0 substitutions\n", + "Cgl0004I processed model has 2310 rows, 1144 columns (1144 integer (572 of which binary)) and 9118 elements\n", + "Cbc0045I MIPStart provided solution with cost 148101\n", + "Cbc0012I Integer solution of 148100.69 found by Reduced search after 0 iterations and 0 nodes (0.19 seconds)\n", + "Cbc0038I Full problem 2310 rows 1144 columns, reduced to 332 rows 175 columns\n", + "Cbc0031I 54 added rows had average density of 106.64815\n", + "Cbc0013I At root node, 54 cuts changed objective from 127486.15 to 129782.95 in 12 passes\n", + "Cbc0014I Cut generator 0 (Probing) - 1 row cuts average 6.0 elements, 0 column cuts (0 active) in 0.052 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 1 (Gomory) - 146 row cuts average 229.8 elements, 0 column cuts (0 active) in 0.117 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 2 (Knapsack) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.021 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 3 (Clique) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.000 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 4 (MixedIntegerRounding2) - 0 row cuts average 0.0 elements, 0 column cuts (0 active) in 0.031 seconds - new frequency is -100\n", + "Cbc0014I Cut generator 5 (FlowCover) - 79 row cuts average 11.3 elements, 0 column cuts (0 active) in 0.010 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 6 (TwoMirCuts) - 188 row cuts average 141.8 elements, 0 column cuts (0 active) in 0.069 seconds - new frequency is 1\n", + "Cbc0014I Cut generator 7 (ZeroHalf) - 14 row cuts average 11.9 elements, 0 column cuts (0 active) in 0.075 seconds - new frequency is -100\n", + "Cbc0010I After 0 nodes, 1 on tree, 148100.69 best solution, best possible 129782.95 (0.79 seconds)\n", + "Cbc0010I After 100 nodes, 59 on tree, 148100.69 best solution, best possible 129955.66 (2.20 seconds)\n", + "Cbc0010I After 200 nodes, 113 on tree, 148100.69 best solution, best possible 129955.66 (2.40 seconds)\n", + "Cbc0010I After 300 nodes, 129 on tree, 148100.69 best solution, best possible 129955.66 (2.54 seconds)\n", + "Cbc0012I Integer solution of 147991.37 found by heuristic after 16583 iterations and 324 nodes (2.58 seconds)\n", + "Cbc0010I After 400 nodes, 170 on tree, 147991.37 best solution, best possible 129955.66 (2.79 seconds)\n", + "Cbc0010I After 500 nodes, 219 on tree, 147991.37 best solution, best possible 129955.66 (3.08 seconds)\n", + "Cbc0010I After 600 nodes, 269 on tree, 147991.37 best solution, best possible 129955.66 (3.36 seconds)\n", + "Cbc0010I After 700 nodes, 318 on tree, 147991.37 best solution, best possible 129955.66 (3.55 seconds)\n", + "Cbc0010I After 800 nodes, 368 on tree, 147991.37 best solution, best possible 129955.66 (3.72 seconds)\n", + "Cbc0012I Integer solution of 143512.79 found by heuristic after 37528 iterations and 881 nodes (3.83 seconds)\n", + "Cbc0012I Integer solution of 143016.87 found by heuristic after 37928 iterations and 891 nodes (3.85 seconds)\n", + "Cbc0010I After 900 nodes, 316 on tree, 143016.87 best solution, best possible 129955.66 (3.86 seconds)\n", + "Cbc0012I Integer solution of 140625.34 found by heuristic after 40868 iterations and 981 nodes (4.00 seconds)\n", + "Cbc0010I After 1000 nodes, 309 on tree, 140625.34 best solution, best possible 129955.66 (4.03 seconds)\n", + "Cbc0010I After 1100 nodes, 355 on tree, 140625.34 best solution, best possible 129955.66 (4.26 seconds)\n", + "Cbc0010I After 1200 nodes, 399 on tree, 140625.34 best solution, best possible 129955.66 (4.45 seconds)\n", + "Cbc0010I After 1300 nodes, 443 on tree, 140625.34 best solution, best possible 129955.66 (4.62 seconds)\n", + "Cbc0010I After 1400 nodes, 493 on tree, 140625.34 best solution, best possible 129955.66 (4.74 seconds)\n", + "Cbc0010I After 1500 nodes, 535 on tree, 140625.34 best solution, best possible 129955.66 (4.86 seconds)\n", + "Cbc0010I After 1600 nodes, 577 on tree, 140625.34 best solution, best possible 129955.66 (5.00 seconds)\n", + "Cbc0010I After 1700 nodes, 626 on tree, 140625.34 best solution, best possible 129955.66 (5.14 seconds)\n", + "Cbc0010I After 1800 nodes, 675 on tree, 140625.34 best solution, best possible 129955.66 (5.27 seconds)\n", + "Cbc0010I After 1900 nodes, 712 on tree, 140625.34 best solution, best possible 129955.66 (5.43 seconds)\n", + "Cbc0010I After 2000 nodes, 748 on tree, 140625.34 best solution, best possible 129955.66 (5.57 seconds)\n", + "Cbc0010I After 2100 nodes, 796 on tree, 140625.34 best solution, best possible 129955.66 (5.70 seconds)\n", + "Cbc0010I After 2200 nodes, 839 on tree, 140625.34 best solution, best possible 129955.66 (5.80 seconds)\n", + "Cbc0010I After 2300 nodes, 893 on tree, 140625.34 best solution, best possible 129955.66 (5.88 seconds)\n", + "Cbc0010I After 2400 nodes, 939 on tree, 140625.34 best solution, best possible 129955.66 (5.97 seconds)\n", + "Cbc0010I After 2500 nodes, 984 on tree, 140625.34 best solution, best possible 129955.66 (6.06 seconds)\n", + "Cbc0010I After 2600 nodes, 1023 on tree, 140625.34 best solution, best possible 129955.66 (6.15 seconds)\n", + "Cbc0010I After 2700 nodes, 1067 on tree, 140625.34 best solution, best possible 129955.66 (6.24 seconds)\n", + "Cbc0010I After 2800 nodes, 1116 on tree, 140625.34 best solution, best possible 129955.66 (6.31 seconds)\n", + "Cbc0010I After 2900 nodes, 1151 on tree, 140625.34 best solution, best possible 129955.66 (6.36 seconds)\n", + "Cbc0010I After 3000 nodes, 1198 on tree, 140625.34 best solution, best possible 129955.66 (6.43 seconds)\n", + "Cbc0010I After 3100 nodes, 1243 on tree, 140625.34 best solution, best possible 129955.66 (6.51 seconds)\n", + "Cbc0010I After 3200 nodes, 1275 on tree, 140625.34 best solution, best possible 129955.66 (6.60 seconds)\n", + "Cbc0010I After 3300 nodes, 1304 on tree, 140625.34 best solution, best possible 129955.66 (6.66 seconds)\n", + "Cbc0010I After 3400 nodes, 1346 on tree, 140625.34 best solution, best possible 129955.66 (6.77 seconds)\n", + "Cbc0010I After 3500 nodes, 1391 on tree, 140625.34 best solution, best possible 129955.66 (6.89 seconds)\n", + "Cbc0010I After 3600 nodes, 1442 on tree, 140625.34 best solution, best possible 129955.66 (7.00 seconds)\n", + "Cbc0010I After 3700 nodes, 1488 on tree, 140625.34 best solution, best possible 129955.66 (7.13 seconds)\n", + "Cbc0010I After 3800 nodes, 1531 on tree, 140625.34 best solution, best possible 129955.66 (7.26 seconds)\n", + "Cbc0010I After 3900 nodes, 1570 on tree, 140625.34 best solution, best possible 129955.66 (7.36 seconds)\n", + "Cbc0010I After 4000 nodes, 1600 on tree, 140625.34 best solution, best possible 129955.66 (7.45 seconds)\n", + "Cbc0010I After 4100 nodes, 1635 on tree, 140625.34 best solution, best possible 129955.66 (7.54 seconds)\n", + "Cbc0010I After 4200 nodes, 1670 on tree, 140625.34 best solution, best possible 129955.66 (7.66 seconds)\n", + "Cbc0010I After 4300 nodes, 1705 on tree, 140625.34 best solution, best possible 129955.66 (7.75 seconds)\n", + "Cbc0010I After 4400 nodes, 1754 on tree, 140625.34 best solution, best possible 129955.66 (7.89 seconds)\n", + "Cbc0010I After 4500 nodes, 1796 on tree, 140625.34 best solution, best possible 129955.66 (8.03 seconds)\n", + "Cbc0010I After 4600 nodes, 1834 on tree, 140625.34 best solution, best possible 129955.66 (8.13 seconds)\n", + "Cbc0012I Integer solution of 139130.29 found by heuristic after 180874 iterations and 4699 nodes (8.24 seconds)\n", + "Cbc0010I After 4700 nodes, 1550 on tree, 139130.29 best solution, best possible 129955.66 (8.24 seconds)\n", + "Cbc0010I After 4800 nodes, 1592 on tree, 139130.29 best solution, best possible 129955.66 (8.43 seconds)\n", + "Cbc0010I After 4900 nodes, 1645 on tree, 139130.29 best solution, best possible 129955.66 (8.61 seconds)\n", + "Cbc0010I After 5000 nodes, 1690 on tree, 139130.29 best solution, best possible 129955.66 (8.76 seconds)\n", + "Cbc0010I After 5100 nodes, 1736 on tree, 139130.29 best solution, best possible 129955.66 (8.89 seconds)\n", + "Cbc0010I After 5200 nodes, 1780 on tree, 139130.29 best solution, best possible 129955.66 (8.98 seconds)\n", + "Cbc0010I After 5300 nodes, 1821 on tree, 139130.29 best solution, best possible 129955.66 (9.05 seconds)\n", + "Cbc0010I After 5400 nodes, 1858 on tree, 139130.29 best solution, best possible 129955.66 (9.15 seconds)\n", + "Cbc0010I After 5500 nodes, 1908 on tree, 139130.29 best solution, best possible 129955.66 (9.23 seconds)\n", + "Cbc0010I After 5600 nodes, 1952 on tree, 139130.29 best solution, best possible 129955.66 (9.32 seconds)\n", + "Cbc0010I After 5700 nodes, 2000 on tree, 139130.29 best solution, best possible 129955.66 (9.39 seconds)\n", + "Cbc0012I Integer solution of 138766.39 found by heuristic after 224235 iterations and 5731 nodes (9.41 seconds)\n", + "Cbc0010I After 5800 nodes, 1933 on tree, 138766.39 best solution, best possible 129955.66 (9.47 seconds)\n", + "Cbc0010I After 5900 nodes, 1976 on tree, 138766.39 best solution, best possible 129955.66 (9.55 seconds)\n", + "Cbc0010I After 6000 nodes, 2009 on tree, 138766.39 best solution, best possible 129955.66 (9.63 seconds)\n", + "Cbc0012I Integer solution of 137927.49 found by heuristic after 237967 iterations and 6097 nodes (9.72 seconds)\n", + "Cbc0010I After 6100 nodes, 1665 on tree, 137927.49 best solution, best possible 129955.66 (9.73 seconds)\n", + "Cbc0010I After 6200 nodes, 1711 on tree, 137927.49 best solution, best possible 129955.66 (9.85 seconds)\n", + "Cbc0010I After 6300 nodes, 1755 on tree, 137927.49 best solution, best possible 129955.66 (10.01 seconds)\n", + "Cbc0010I After 6400 nodes, 1794 on tree, 137927.49 best solution, best possible 129955.66 (10.13 seconds)\n", + "Cbc0010I After 6500 nodes, 1824 on tree, 137927.49 best solution, best possible 129955.66 (10.24 seconds)\n", + "Cbc0010I After 6600 nodes, 1857 on tree, 137927.49 best solution, best possible 129955.66 (10.33 seconds)\n", + "Cbc0010I After 6700 nodes, 1904 on tree, 137927.49 best solution, best possible 129955.66 (10.50 seconds)\n", + "Cbc0010I After 6800 nodes, 1946 on tree, 137927.49 best solution, best possible 129955.66 (10.68 seconds)\n", + "Cbc0010I After 6900 nodes, 1986 on tree, 137927.49 best solution, best possible 129955.66 (10.80 seconds)\n", + "Cbc0010I After 7000 nodes, 2020 on tree, 137927.49 best solution, best possible 129955.66 (10.91 seconds)\n", + "Cbc0010I After 7100 nodes, 2046 on tree, 137927.49 best solution, best possible 129955.66 (11.02 seconds)\n", + "Cbc0010I After 7200 nodes, 2070 on tree, 137927.49 best solution, best possible 129955.66 (11.12 seconds)\n", + "Cbc0010I After 7300 nodes, 2106 on tree, 137927.49 best solution, best possible 129955.66 (11.27 seconds)\n", + "Cbc0010I After 7400 nodes, 2141 on tree, 137927.49 best solution, best possible 129955.66 (11.37 seconds)\n", + "Cbc0010I After 7500 nodes, 2168 on tree, 137927.49 best solution, best possible 129955.66 (11.49 seconds)\n", + "Cbc0010I After 7600 nodes, 2202 on tree, 137927.49 best solution, best possible 129955.66 (11.61 seconds)\n", + "Cbc0010I After 7700 nodes, 2241 on tree, 137927.49 best solution, best possible 129955.66 (11.73 seconds)\n", + "Cbc0010I After 7800 nodes, 2275 on tree, 137927.49 best solution, best possible 129955.66 (11.88 seconds)\n", + "Cbc0010I After 7900 nodes, 2310 on tree, 137927.49 best solution, best possible 129955.66 (12.01 seconds)\n", + "Cbc0010I After 8000 nodes, 2355 on tree, 137927.49 best solution, best possible 129955.66 (12.11 seconds)\n", + "Cbc0010I After 8100 nodes, 2386 on tree, 137927.49 best solution, best possible 129955.66 (12.26 seconds)\n", + "Cbc0010I After 8200 nodes, 2421 on tree, 137927.49 best solution, best possible 129955.66 (12.38 seconds)\n", + "Cbc0010I After 8300 nodes, 2443 on tree, 137927.49 best solution, best possible 129955.66 (12.46 seconds)\n", + "Cbc0010I After 8400 nodes, 2472 on tree, 137927.49 best solution, best possible 129955.66 (12.61 seconds)\n", + "Cbc0010I After 8500 nodes, 2510 on tree, 137927.49 best solution, best possible 129955.66 (12.74 seconds)\n", + "Cbc0010I After 8600 nodes, 2538 on tree, 137927.49 best solution, best possible 129955.66 (12.86 seconds)\n", + "Cbc0010I After 8700 nodes, 2563 on tree, 137927.49 best solution, best possible 129955.66 (12.94 seconds)\n", + "Cbc0010I After 8800 nodes, 2591 on tree, 137927.49 best solution, best possible 129955.66 (13.04 seconds)\n", + "Cbc0010I After 8900 nodes, 2632 on tree, 137927.49 best solution, best possible 129955.66 (13.15 seconds)\n", + "Cbc0010I After 9000 nodes, 2659 on tree, 137927.49 best solution, best possible 129955.66 (13.24 seconds)\n", + "Cbc0010I After 9100 nodes, 2697 on tree, 137927.49 best solution, best possible 129955.66 (13.36 seconds)\n", + "Cbc0010I After 9200 nodes, 2729 on tree, 137927.49 best solution, best possible 129955.66 (13.49 seconds)\n", + "Cbc0010I After 9300 nodes, 2757 on tree, 137927.49 best solution, best possible 129955.66 (13.60 seconds)\n", + "Cbc0010I After 9400 nodes, 2782 on tree, 137927.49 best solution, best possible 129955.66 (13.69 seconds)\n", + "Cbc0010I After 9500 nodes, 2823 on tree, 137927.49 best solution, best possible 129955.66 (13.85 seconds)\n", + "Cbc0010I After 9600 nodes, 2849 on tree, 137927.49 best solution, best possible 129955.66 (13.97 seconds)\n", + "Cbc0010I After 9700 nodes, 2884 on tree, 137927.49 best solution, best possible 129955.66 (14.10 seconds)\n", + "Cbc0010I After 9800 nodes, 2932 on tree, 137927.49 best solution, best possible 129955.66 (14.23 seconds)\n", + "Cbc0010I After 9900 nodes, 2974 on tree, 137927.49 best solution, best possible 129955.66 (14.35 seconds)\n", + "Cbc0010I After 10000 nodes, 3014 on tree, 137927.49 best solution, best possible 129955.66 (14.50 seconds)\n", + "Cbc0010I After 10100 nodes, 3048 on tree, 137927.49 best solution, best possible 129955.66 (14.64 seconds)\n", + "Cbc0010I After 10200 nodes, 3080 on tree, 137927.49 best solution, best possible 129955.66 (14.74 seconds)\n", + "Cbc0010I After 10300 nodes, 3108 on tree, 137927.49 best solution, best possible 129955.66 (14.84 seconds)\n", + "Cbc0010I After 10400 nodes, 3135 on tree, 137927.49 best solution, best possible 129955.66 (14.94 seconds)\n", + "Cbc0010I After 10500 nodes, 3177 on tree, 137927.49 best solution, best possible 129955.66 (15.07 seconds)\n", + "Cbc0010I After 10600 nodes, 3210 on tree, 137927.49 best solution, best possible 129955.66 (15.17 seconds)\n", + "Cbc0010I After 10700 nodes, 3235 on tree, 137927.49 best solution, best possible 129955.66 (15.26 seconds)\n", + "Cbc0010I After 10800 nodes, 3269 on tree, 137927.49 best solution, best possible 129955.66 (15.34 seconds)\n", + "Cbc0010I After 10900 nodes, 3283 on tree, 137927.49 best solution, best possible 129955.66 (15.43 seconds)\n", + "Cbc0010I After 11000 nodes, 3322 on tree, 137927.49 best solution, best possible 129955.66 (15.58 seconds)\n", + "Cbc0010I After 11100 nodes, 3372 on tree, 137927.49 best solution, best possible 130075.74 (16.30 seconds)\n", + "Cbc0010I After 11200 nodes, 3419 on tree, 137927.49 best solution, best possible 130588.48 (16.63 seconds)\n", + "Cbc0010I After 11300 nodes, 3466 on tree, 137927.49 best solution, best possible 130698.66 (16.99 seconds)\n", + "Cbc0010I After 11400 nodes, 3513 on tree, 137927.49 best solution, best possible 130803.52 (17.29 seconds)\n", + "Cbc0010I After 11500 nodes, 3564 on tree, 137927.49 best solution, best possible 130879.27 (17.55 seconds)\n", + "Cbc0010I After 11600 nodes, 3612 on tree, 137927.49 best solution, best possible 130909.99 (17.80 seconds)\n", + "Cbc0010I After 11700 nodes, 3661 on tree, 137927.49 best solution, best possible 130961.82 (18.03 seconds)\n", + "Cbc0010I After 11800 nodes, 3711 on tree, 137927.49 best solution, best possible 131016.42 (18.26 seconds)\n", + "Cbc0010I After 11900 nodes, 3759 on tree, 137927.49 best solution, best possible 131049.32 (18.54 seconds)\n", + "Cbc0010I After 12000 nodes, 3807 on tree, 137927.49 best solution, best possible 131081.91 (18.78 seconds)\n", + "Cbc0010I After 12100 nodes, 3855 on tree, 137927.49 best solution, best possible 131108.51 (19.02 seconds)\n", + "Cbc0010I After 12200 nodes, 3904 on tree, 137927.49 best solution, best possible 131108.51 (19.25 seconds)\n", + "Cbc0010I After 12300 nodes, 3955 on tree, 137927.49 best solution, best possible 131165.06 (19.45 seconds)\n", + "Cbc0010I After 12400 nodes, 4005 on tree, 137927.49 best solution, best possible 131185.24 (19.65 seconds)\n", + "Cbc0010I After 12500 nodes, 4053 on tree, 137927.49 best solution, best possible 131197.71 (19.87 seconds)\n", + "Cbc0010I After 12600 nodes, 4101 on tree, 137927.49 best solution, best possible 131222.65 (20.02 seconds)\n", + "Cbc0010I After 12700 nodes, 4149 on tree, 137927.49 best solution, best possible 131238.08 (20.17 seconds)\n", + "Cbc0010I After 12800 nodes, 4197 on tree, 137927.49 best solution, best possible 131266.23 (20.34 seconds)\n", + "Cbc0010I After 12900 nodes, 4247 on tree, 137927.49 best solution, best possible 131283.14 (20.54 seconds)\n", + "Cbc0010I After 13000 nodes, 4298 on tree, 137927.49 best solution, best possible 131292.85 (20.71 seconds)\n", + "Cbc0010I After 13100 nodes, 4346 on tree, 137927.49 best solution, best possible 131295.94 (20.84 seconds)\n", + "Cbc0010I After 13200 nodes, 4394 on tree, 137927.49 best solution, best possible 131295.94 (20.98 seconds)\n", + "Cbc0010I After 13300 nodes, 4442 on tree, 137927.49 best solution, best possible 131295.94 (21.11 seconds)\n", + "Cbc0010I After 13400 nodes, 4491 on tree, 137927.49 best solution, best possible 131295.94 (21.25 seconds)\n", + "Cbc0010I After 13500 nodes, 4540 on tree, 137927.49 best solution, best possible 131295.94 (21.42 seconds)\n", + "Cbc0010I After 13600 nodes, 4585 on tree, 137927.49 best solution, best possible 131295.94 (21.55 seconds)\n", + "Cbc0010I After 13700 nodes, 4634 on tree, 137927.49 best solution, best possible 131295.94 (21.68 seconds)\n", + "Cbc0010I After 13800 nodes, 4682 on tree, 137927.49 best solution, best possible 131295.94 (21.81 seconds)\n", + "Cbc0010I After 13900 nodes, 4731 on tree, 137927.49 best solution, best possible 131295.94 (21.95 seconds)\n", + "Cbc0010I After 14000 nodes, 4777 on tree, 137927.49 best solution, best possible 131295.94 (22.10 seconds)\n", + "Cbc0010I After 14100 nodes, 4828 on tree, 137927.49 best solution, best possible 131310.12 (22.31 seconds)\n", + "Cbc0010I After 14200 nodes, 4877 on tree, 137927.49 best solution, best possible 131324.7 (22.52 seconds)\n", + "Cbc0010I After 14300 nodes, 4925 on tree, 137927.49 best solution, best possible 131333.34 (22.73 seconds)\n", + "Cbc0010I After 14400 nodes, 4975 on tree, 137927.49 best solution, best possible 131364.11 (22.92 seconds)\n", + "Cbc0010I After 14500 nodes, 5025 on tree, 137927.49 best solution, best possible 131378.36 (23.09 seconds)\n", + "Cbc0010I After 14600 nodes, 5074 on tree, 137927.49 best solution, best possible 131395.89 (23.29 seconds)\n", + "Cbc0010I After 14700 nodes, 5123 on tree, 137927.49 best solution, best possible 131411.26 (23.45 seconds)\n", + "Cbc0010I After 14800 nodes, 5171 on tree, 137927.49 best solution, best possible 131418.17 (23.65 seconds)\n", + "Cbc0010I After 14900 nodes, 5219 on tree, 137927.49 best solution, best possible 131442.43 (23.83 seconds)\n", + "Cbc0010I After 15000 nodes, 5267 on tree, 137927.49 best solution, best possible 131448.6 (24.07 seconds)\n", + "Cbc0010I After 15100 nodes, 5316 on tree, 137927.49 best solution, best possible 131453.95 (24.23 seconds)\n", + "Cbc0010I After 15200 nodes, 5367 on tree, 137927.49 best solution, best possible 131472.5 (24.40 seconds)\n", + "Cbc0010I After 15300 nodes, 5415 on tree, 137927.49 best solution, best possible 131481.96 (24.57 seconds)\n", + "Cbc0010I After 15400 nodes, 5464 on tree, 137927.49 best solution, best possible 131493.71 (24.75 seconds)\n", + "Cbc0010I After 15500 nodes, 5512 on tree, 137927.49 best solution, best possible 131499.05 (24.92 seconds)\n", + "Cbc0010I After 15600 nodes, 5562 on tree, 137927.49 best solution, best possible 131510.56 (25.08 seconds)\n", + "Cbc0010I After 15700 nodes, 5612 on tree, 137927.49 best solution, best possible 131515.05 (25.24 seconds)\n", + "Cbc0010I After 15800 nodes, 5663 on tree, 137927.49 best solution, best possible 131529.64 (25.43 seconds)\n", + "Cbc0010I After 15900 nodes, 5708 on tree, 137927.49 best solution, best possible 131538.74 (25.58 seconds)\n", + "Cbc0010I After 16000 nodes, 5760 on tree, 137927.49 best solution, best possible 131538.74 (25.85 seconds)\n", + "Cbc0010I After 16100 nodes, 5810 on tree, 137927.49 best solution, best possible 131556.33 (26.01 seconds)\n", + "Cbc0010I After 16200 nodes, 5860 on tree, 137927.49 best solution, best possible 131566.27 (26.18 seconds)\n", + "Cbc0010I After 16300 nodes, 5909 on tree, 137927.49 best solution, best possible 131575.64 (26.35 seconds)\n", + "Cbc0010I After 16400 nodes, 5958 on tree, 137927.49 best solution, best possible 131585.42 (26.55 seconds)\n", + "Cbc0010I After 16500 nodes, 6010 on tree, 137927.49 best solution, best possible 131592.8 (26.73 seconds)\n", + "Cbc0010I After 16600 nodes, 6058 on tree, 137927.49 best solution, best possible 131600.26 (26.88 seconds)\n", + "Cbc0010I After 16700 nodes, 6106 on tree, 137927.49 best solution, best possible 131610.88 (27.05 seconds)\n", + "Cbc0010I After 16800 nodes, 6154 on tree, 137927.49 best solution, best possible 131618.99 (27.21 seconds)\n", + "Cbc0010I After 16900 nodes, 6201 on tree, 137927.49 best solution, best possible 131628.02 (27.37 seconds)\n", + "Cbc0010I After 17000 nodes, 6253 on tree, 137927.49 best solution, best possible 131635.52 (27.56 seconds)\n", + "Cbc0010I After 17100 nodes, 6303 on tree, 137927.49 best solution, best possible 131636.36 (27.70 seconds)\n", + "Cbc0010I After 17200 nodes, 6350 on tree, 137927.49 best solution, best possible 131636.36 (27.80 seconds)\n", + "Cbc0010I After 17300 nodes, 6403 on tree, 137927.49 best solution, best possible 131636.36 (27.93 seconds)\n", + "Cbc0010I After 17400 nodes, 6451 on tree, 137927.49 best solution, best possible 131636.36 (28.06 seconds)\n", + "Cbc0010I After 17500 nodes, 6500 on tree, 137927.49 best solution, best possible 131636.36 (28.18 seconds)\n", + "Cbc0010I After 17600 nodes, 6547 on tree, 137927.49 best solution, best possible 131636.36 (28.29 seconds)\n", + "Cbc0010I After 17700 nodes, 6594 on tree, 137927.49 best solution, best possible 131636.36 (28.38 seconds)\n", + "Cbc0010I After 17800 nodes, 6643 on tree, 137927.49 best solution, best possible 131636.36 (28.49 seconds)\n", + "Cbc0010I After 17900 nodes, 6691 on tree, 137927.49 best solution, best possible 131636.36 (28.63 seconds)\n", + "Cbc0010I After 18000 nodes, 6740 on tree, 137927.49 best solution, best possible 131636.36 (28.79 seconds)\n", + "Cbc0010I After 18100 nodes, 6789 on tree, 137927.49 best solution, best possible 131640.68 (28.94 seconds)\n", + "Cbc0010I After 18200 nodes, 6838 on tree, 137927.49 best solution, best possible 131649.79 (29.10 seconds)\n", + "Cbc0010I After 18300 nodes, 6889 on tree, 137927.49 best solution, best possible 131655.65 (29.29 seconds)\n", + "Cbc0010I After 18400 nodes, 6938 on tree, 137927.49 best solution, best possible 131666.34 (29.44 seconds)\n", + "Cbc0010I After 18500 nodes, 6990 on tree, 137927.49 best solution, best possible 131672.84 (29.60 seconds)\n", + "Cbc0010I After 18600 nodes, 7036 on tree, 137927.49 best solution, best possible 131678.78 (29.72 seconds)\n", + "Cbc0010I After 18700 nodes, 7085 on tree, 137927.49 best solution, best possible 131685.96 (29.92 seconds)\n", + "Cbc0010I After 18800 nodes, 7133 on tree, 137927.49 best solution, best possible 131692.37 (30.08 seconds)\n", + "Cbc0010I After 18900 nodes, 7185 on tree, 137927.49 best solution, best possible 131700 (30.27 seconds)\n", + "Cbc0010I After 19000 nodes, 7235 on tree, 137927.49 best solution, best possible 131705.53 (30.43 seconds)\n", + "Cbc0010I After 19100 nodes, 7284 on tree, 137927.49 best solution, best possible 131711.06 (30.58 seconds)\n", + "Cbc0010I After 19200 nodes, 7335 on tree, 137927.49 best solution, best possible 131716.62 (30.77 seconds)\n", + "Cbc0010I After 19300 nodes, 7383 on tree, 137927.49 best solution, best possible 131721.41 (30.88 seconds)\n", + "Cbc0010I After 19400 nodes, 7432 on tree, 137927.49 best solution, best possible 131728.76 (31.04 seconds)\n", + "Cbc0010I After 19500 nodes, 7481 on tree, 137927.49 best solution, best possible 131734.77 (31.20 seconds)\n", + "Cbc0010I After 19600 nodes, 7529 on tree, 137927.49 best solution, best possible 131739.41 (31.34 seconds)\n", + "Cbc0010I After 19700 nodes, 7579 on tree, 137927.49 best solution, best possible 131744.42 (31.51 seconds)\n", + "Cbc0010I After 19800 nodes, 7629 on tree, 137927.49 best solution, best possible 131748.93 (31.66 seconds)\n", + "Cbc0010I After 19900 nodes, 7678 on tree, 137927.49 best solution, best possible 131755.22 (31.85 seconds)\n", + "Cbc0010I After 20000 nodes, 7724 on tree, 137927.49 best solution, best possible 131761.16 (32.01 seconds)\n", + "Cbc0010I After 20100 nodes, 7776 on tree, 137927.49 best solution, best possible 131767.18 (32.18 seconds)\n", + "Cbc0010I After 20200 nodes, 7823 on tree, 137927.49 best solution, best possible 131769.92 (32.36 seconds)\n", + "Cbc0010I After 20300 nodes, 7873 on tree, 137927.49 best solution, best possible 131777.32 (32.54 seconds)\n", + "Cbc0010I After 20400 nodes, 7921 on tree, 137927.49 best solution, best possible 131782.69 (32.72 seconds)\n", + "Cbc0010I After 20500 nodes, 7971 on tree, 137927.49 best solution, best possible 131786.48 (32.92 seconds)\n", + "Cbc0010I After 20600 nodes, 8019 on tree, 137927.49 best solution, best possible 131791.55 (33.08 seconds)\n", + "Cbc0010I After 20700 nodes, 8071 on tree, 137927.49 best solution, best possible 131797.02 (33.29 seconds)\n", + "Cbc0010I After 20800 nodes, 8118 on tree, 137927.49 best solution, best possible 131798.24 (33.46 seconds)\n", + "Cbc0010I After 20900 nodes, 8169 on tree, 137927.49 best solution, best possible 131808.2 (33.65 seconds)\n", + "Cbc0010I After 21000 nodes, 8218 on tree, 137927.49 best solution, best possible 131813.51 (33.82 seconds)\n", + "Cbc0010I After 21100 nodes, 8269 on tree, 137927.49 best solution, best possible 131814.59 (33.96 seconds)\n", + "Cbc0010I After 21200 nodes, 8317 on tree, 137927.49 best solution, best possible 131814.59 (34.09 seconds)\n", + "Cbc0010I After 21300 nodes, 8364 on tree, 137927.49 best solution, best possible 131814.59 (34.21 seconds)\n", + "Cbc0010I After 21400 nodes, 8413 on tree, 137927.49 best solution, best possible 131814.59 (34.35 seconds)\n", + "Cbc0010I After 21500 nodes, 8459 on tree, 137927.49 best solution, best possible 131814.59 (34.48 seconds)\n", + "Cbc0010I After 21600 nodes, 8510 on tree, 137927.49 best solution, best possible 131814.59 (34.62 seconds)\n", + "Cbc0010I After 21700 nodes, 8558 on tree, 137927.49 best solution, best possible 131814.59 (34.76 seconds)\n", + "Cbc0010I After 21800 nodes, 8599 on tree, 137927.49 best solution, best possible 131814.59 (34.91 seconds)\n", + "Cbc0010I After 21900 nodes, 8648 on tree, 137927.49 best solution, best possible 131814.59 (35.06 seconds)\n", + "Cbc0010I After 22000 nodes, 8695 on tree, 137927.49 best solution, best possible 131814.59 (35.22 seconds)\n", + "Cbc0010I After 22100 nodes, 8741 on tree, 137927.49 best solution, best possible 131818.41 (35.46 seconds)\n", + "Cbc0010I After 22200 nodes, 8790 on tree, 137927.49 best solution, best possible 131822.06 (35.68 seconds)\n", + "Cbc0010I After 22300 nodes, 8841 on tree, 137927.49 best solution, best possible 131827.53 (35.96 seconds)\n", + "Cbc0010I After 22400 nodes, 8890 on tree, 137927.49 best solution, best possible 131832.88 (36.16 seconds)\n", + "Cbc0010I After 22500 nodes, 8937 on tree, 137927.49 best solution, best possible 131838.56 (36.32 seconds)\n", + "Cbc0010I After 22600 nodes, 8985 on tree, 137927.49 best solution, best possible 131842.63 (36.50 seconds)\n", + "Cbc0010I After 22700 nodes, 9034 on tree, 137927.49 best solution, best possible 131848 (36.71 seconds)\n", + "Cbc0010I After 22800 nodes, 9086 on tree, 137927.49 best solution, best possible 131851.84 (36.92 seconds)\n", + "Cbc0010I After 22900 nodes, 9135 on tree, 137927.49 best solution, best possible 131854.88 (37.12 seconds)\n", + "Cbc0010I After 23000 nodes, 9185 on tree, 137927.49 best solution, best possible 131862.7 (37.31 seconds)\n", + "Cbc0010I After 23100 nodes, 9236 on tree, 137927.49 best solution, best possible 131869 (37.48 seconds)\n", + "Cbc0010I After 23200 nodes, 9285 on tree, 137927.49 best solution, best possible 131871.97 (37.67 seconds)\n", + "Cbc0010I After 23300 nodes, 9331 on tree, 137927.49 best solution, best possible 131876.13 (37.82 seconds)\n", + "Cbc0010I After 23400 nodes, 9381 on tree, 137927.49 best solution, best possible 131880.19 (37.99 seconds)\n", + "Cbc0010I After 23500 nodes, 9429 on tree, 137927.49 best solution, best possible 131883.58 (38.14 seconds)\n", + "Cbc0010I After 23600 nodes, 9476 on tree, 137927.49 best solution, best possible 131888.24 (38.35 seconds)\n", + "Cbc0010I After 23700 nodes, 9527 on tree, 137927.49 best solution, best possible 131892.09 (38.53 seconds)\n", + "Cbc0010I After 23800 nodes, 9576 on tree, 137927.49 best solution, best possible 131897.59 (38.72 seconds)\n", + "Cbc0010I After 23900 nodes, 9623 on tree, 137927.49 best solution, best possible 131900.82 (38.87 seconds)\n", + "Cbc0010I After 24000 nodes, 9673 on tree, 137927.49 best solution, best possible 131906.11 (39.07 seconds)\n", + "Cbc0010I After 24100 nodes, 9722 on tree, 137927.49 best solution, best possible 131909.77 (39.24 seconds)\n", + "Cbc0010I After 24200 nodes, 9772 on tree, 137927.49 best solution, best possible 131912.86 (39.40 seconds)\n", + "Cbc0010I After 24300 nodes, 9819 on tree, 137927.49 best solution, best possible 131916.24 (39.55 seconds)\n", + "Cbc0010I After 24400 nodes, 9867 on tree, 137927.49 best solution, best possible 131920.33 (39.70 seconds)\n", + "Cbc0010I After 24500 nodes, 9918 on tree, 137927.49 best solution, best possible 131921.66 (39.90 seconds)\n", + "Cbc0010I After 24600 nodes, 9968 on tree, 137927.49 best solution, best possible 131927.61 (40.07 seconds)\n", + "Cbc0010I After 24700 nodes, 10014 on tree, 137927.49 best solution, best possible 131932.98 (40.27 seconds)\n", + "Cbc0010I After 24800 nodes, 10064 on tree, 137927.49 best solution, best possible 131935.39 (40.45 seconds)\n", + "Cbc0010I After 24900 nodes, 10113 on tree, 137927.49 best solution, best possible 131940.28 (40.65 seconds)\n", + "Cbc0010I After 25000 nodes, 10162 on tree, 137927.49 best solution, best possible 131943.08 (40.88 seconds)\n", + "Cbc0010I After 25100 nodes, 10212 on tree, 137927.49 best solution, best possible 131946.87 (41.11 seconds)\n", + "Cbc0010I After 25200 nodes, 10260 on tree, 137927.49 best solution, best possible 131950.79 (41.34 seconds)\n", + "Cbc0010I After 25300 nodes, 10308 on tree, 137927.49 best solution, best possible 131955.67 (41.55 seconds)\n", + "Cbc0010I After 25400 nodes, 10358 on tree, 137927.49 best solution, best possible 131959.33 (41.71 seconds)\n", + "Cbc0010I After 25500 nodes, 10405 on tree, 137927.49 best solution, best possible 131962.8 (41.91 seconds)\n", + "Cbc0010I After 25600 nodes, 10454 on tree, 137927.49 best solution, best possible 131967.16 (42.08 seconds)\n", + "Cbc0010I After 25700 nodes, 10504 on tree, 137927.49 best solution, best possible 131970.22 (42.32 seconds)\n", + "Cbc0010I After 25800 nodes, 10552 on tree, 137927.49 best solution, best possible 131974.95 (42.53 seconds)\n", + "Cbc0010I After 25900 nodes, 10602 on tree, 137927.49 best solution, best possible 131979.33 (42.75 seconds)\n", + "Cbc0010I After 26000 nodes, 10651 on tree, 137927.49 best solution, best possible 131984.09 (42.96 seconds)\n", + "Cbc0010I After 26100 nodes, 10701 on tree, 137927.49 best solution, best possible 131984.5 (43.16 seconds)\n", + "Cbc0010I After 26200 nodes, 10751 on tree, 137927.49 best solution, best possible 131984.5 (43.32 seconds)\n", + "Cbc0010I After 26300 nodes, 10786 on tree, 137927.49 best solution, best possible 131984.5 (43.46 seconds)\n", + "Cbc0010I After 26400 nodes, 10831 on tree, 137927.49 best solution, best possible 131984.5 (43.62 seconds)\n", + "Cbc0010I After 26500 nodes, 10877 on tree, 137927.49 best solution, best possible 131984.5 (43.77 seconds)\n", + "Cbc0010I After 26600 nodes, 10918 on tree, 137927.49 best solution, best possible 131984.5 (43.91 seconds)\n", + "Cbc0010I After 26700 nodes, 10969 on tree, 137927.49 best solution, best possible 131984.5 (44.06 seconds)\n", + "Cbc0010I After 26800 nodes, 11012 on tree, 137927.49 best solution, best possible 131984.5 (44.22 seconds)\n", + "Cbc0010I After 26900 nodes, 11056 on tree, 137927.49 best solution, best possible 131984.5 (44.37 seconds)\n", + "Cbc0010I After 27000 nodes, 11102 on tree, 137927.49 best solution, best possible 131984.5 (44.51 seconds)\n", + "Cbc0010I After 27100 nodes, 11154 on tree, 137927.49 best solution, best possible 131984.5 (44.68 seconds)\n", + "Cbc0010I After 27200 nodes, 11196 on tree, 137927.49 best solution, best possible 131984.5 (44.80 seconds)\n", + "Cbc0010I After 27300 nodes, 11244 on tree, 137927.49 best solution, best possible 131984.5 (44.96 seconds)\n", + "Cbc0010I After 27400 nodes, 11286 on tree, 137927.49 best solution, best possible 131984.5 (45.08 seconds)\n", + "Cbc0010I After 27500 nodes, 11331 on tree, 137927.49 best solution, best possible 131984.5 (45.21 seconds)\n", + "Cbc0010I After 27600 nodes, 11375 on tree, 137927.49 best solution, best possible 131984.5 (45.34 seconds)\n", + "Cbc0010I After 27700 nodes, 11415 on tree, 137927.49 best solution, best possible 131984.5 (45.52 seconds)\n", + "Cbc0010I After 27800 nodes, 11456 on tree, 137927.49 best solution, best possible 131984.5 (45.63 seconds)\n", + "Cbc0010I After 27900 nodes, 11502 on tree, 137927.49 best solution, best possible 131984.5 (45.78 seconds)\n", + "Cbc0010I After 28000 nodes, 11546 on tree, 137927.49 best solution, best possible 131984.5 (45.92 seconds)\n", + "Cbc0012I Integer solution of 137692.85 found by heuristic after 1419933 iterations and 28044 nodes (45.96 seconds)\n", + "Cbc0010I After 28100 nodes, 11327 on tree, 137692.85 best solution, best possible 131984.5 (46.02 seconds)\n", + "Cbc0010I After 28200 nodes, 11361 on tree, 137692.85 best solution, best possible 131984.5 (46.13 seconds)\n", + "Cbc0010I After 28300 nodes, 11405 on tree, 137692.85 best solution, best possible 131984.5 (46.25 seconds)\n", + "Cbc0010I After 28400 nodes, 11446 on tree, 137692.85 best solution, best possible 131984.5 (46.40 seconds)\n", + "Cbc0010I After 28500 nodes, 11491 on tree, 137692.85 best solution, best possible 131984.5 (46.54 seconds)\n", + "Cbc0010I After 28600 nodes, 11542 on tree, 137692.85 best solution, best possible 131984.5 (46.66 seconds)\n", + "Cbc0010I After 28700 nodes, 11577 on tree, 137692.85 best solution, best possible 131984.5 (46.74 seconds)\n", + "Cbc0010I After 28800 nodes, 11618 on tree, 137692.85 best solution, best possible 131984.5 (46.87 seconds)\n", + "Cbc0012I Integer solution of 137292.37 found by heuristic after 1458675 iterations and 28886 nodes (47.02 seconds)\n", + "Cbc0010I After 28900 nodes, 11218 on tree, 137292.37 best solution, best possible 131984.5 (47.05 seconds)\n", + "Cbc0010I After 29000 nodes, 11269 on tree, 137292.37 best solution, best possible 131984.5 (47.17 seconds)\n", + "Cbc0010I After 29100 nodes, 11315 on tree, 137292.37 best solution, best possible 131987.29 (47.34 seconds)\n", + "Cbc0010I After 29200 nodes, 11363 on tree, 137292.37 best solution, best possible 131992.01 (47.55 seconds)\n", + "Cbc0010I After 29300 nodes, 11414 on tree, 137292.37 best solution, best possible 131996.33 (47.75 seconds)\n", + "Cbc0010I After 29400 nodes, 11463 on tree, 137292.37 best solution, best possible 131999.66 (47.94 seconds)\n", + "Cbc0010I After 29500 nodes, 11509 on tree, 137292.37 best solution, best possible 132003.88 (48.13 seconds)\n", + "Cbc0010I After 29600 nodes, 11558 on tree, 137292.37 best solution, best possible 132008.39 (48.30 seconds)\n", + "Cbc0010I After 29700 nodes, 11603 on tree, 137292.37 best solution, best possible 132012.75 (48.50 seconds)\n", + "Cbc0010I After 29800 nodes, 11651 on tree, 137292.37 best solution, best possible 132017.3 (48.73 seconds)\n", + "Cbc0010I After 29900 nodes, 11701 on tree, 137292.37 best solution, best possible 132021.28 (48.95 seconds)\n", + "Cbc0010I After 30000 nodes, 11752 on tree, 137292.37 best solution, best possible 132022.93 (49.17 seconds)\n", + "Cbc0010I After 30100 nodes, 11795 on tree, 137292.37 best solution, best possible 132024.7 (49.36 seconds)\n", + "Cbc0010I After 30200 nodes, 11839 on tree, 137292.37 best solution, best possible 132024.7 (49.50 seconds)\n", + "Cbc0010I After 30300 nodes, 11876 on tree, 137292.37 best solution, best possible 132024.7 (49.68 seconds)\n", + "Cbc0010I After 30400 nodes, 11922 on tree, 137292.37 best solution, best possible 132024.7 (49.85 seconds)\n", + "Cbc0010I After 30500 nodes, 11966 on tree, 137292.37 best solution, best possible 132024.7 (50.01 seconds)\n", + "Cbc0010I After 30600 nodes, 12008 on tree, 137292.37 best solution, best possible 132024.7 (50.16 seconds)\n", + "Cbc0010I After 30700 nodes, 12055 on tree, 137292.37 best solution, best possible 132024.7 (50.31 seconds)\n", + "Cbc0010I After 30800 nodes, 12101 on tree, 137292.37 best solution, best possible 132024.7 (50.47 seconds)\n", + "Cbc0010I After 30900 nodes, 12145 on tree, 137292.37 best solution, best possible 132024.7 (50.62 seconds)\n", + "Cbc0010I After 31000 nodes, 12190 on tree, 137292.37 best solution, best possible 132024.7 (50.79 seconds)\n", + "Cbc0010I After 31100 nodes, 12235 on tree, 137292.37 best solution, best possible 132024.7 (50.91 seconds)\n", + "Cbc0010I After 31200 nodes, 12273 on tree, 137292.37 best solution, best possible 132024.7 (51.05 seconds)\n", + "Cbc0010I After 31300 nodes, 12322 on tree, 137292.37 best solution, best possible 132024.7 (51.19 seconds)\n", + "Cbc0010I After 31400 nodes, 12367 on tree, 137292.37 best solution, best possible 132024.7 (51.35 seconds)\n", + "Cbc0010I After 31500 nodes, 12409 on tree, 137292.37 best solution, best possible 132024.7 (51.48 seconds)\n", + "Cbc0010I After 31600 nodes, 12449 on tree, 137292.37 best solution, best possible 132024.7 (51.63 seconds)\n", + "Cbc0010I After 31700 nodes, 12499 on tree, 137292.37 best solution, best possible 132024.7 (51.79 seconds)\n", + "Cbc0010I After 31800 nodes, 12545 on tree, 137292.37 best solution, best possible 132024.7 (51.93 seconds)\n", + "Cbc0010I After 31900 nodes, 12586 on tree, 137292.37 best solution, best possible 132024.7 (52.08 seconds)\n", + "Cbc0010I After 32000 nodes, 12629 on tree, 137292.37 best solution, best possible 132024.7 (52.23 seconds)\n", + "Cbc0012I Integer solution of 137232.33 found by heuristic after 1624810 iterations and 32002 nodes (52.23 seconds)\n", + "Cbc0010I After 32100 nodes, 12592 on tree, 137232.33 best solution, best possible 132024.7 (52.39 seconds)\n", + "Cbc0010I After 32200 nodes, 12627 on tree, 137232.33 best solution, best possible 132024.7 (52.52 seconds)\n", + "Cbc0010I After 32300 nodes, 12642 on tree, 137232.33 best solution, best possible 132024.7 (52.63 seconds)\n", + "Cbc0010I After 32400 nodes, 12681 on tree, 137232.33 best solution, best possible 132024.7 (52.78 seconds)\n", + "Cbc0010I After 32500 nodes, 12716 on tree, 137232.33 best solution, best possible 132024.7 (52.92 seconds)\n", + "Cbc0010I After 32600 nodes, 12749 on tree, 137232.33 best solution, best possible 132024.7 (53.05 seconds)\n", + "Cbc0010I After 32700 nodes, 12796 on tree, 137232.33 best solution, best possible 132024.7 (53.21 seconds)\n", + "Cbc0010I After 32800 nodes, 12840 on tree, 137232.33 best solution, best possible 132024.7 (53.35 seconds)\n", + "Cbc0010I After 32900 nodes, 12884 on tree, 137232.33 best solution, best possible 132024.7 (53.51 seconds)\n", + "Cbc0010I After 33000 nodes, 12927 on tree, 137232.33 best solution, best possible 132024.7 (53.62 seconds)\n", + "Cbc0010I After 33100 nodes, 12977 on tree, 137232.33 best solution, best possible 132026.85 (53.81 seconds)\n", + "Cbc0010I After 33200 nodes, 13027 on tree, 137232.33 best solution, best possible 132031.14 (53.96 seconds)\n", + "Cbc0010I After 33300 nodes, 13074 on tree, 137232.33 best solution, best possible 132035.81 (54.13 seconds)\n", + "Cbc0010I After 33400 nodes, 13123 on tree, 137232.33 best solution, best possible 132039.6 (54.32 seconds)\n", + "Cbc0010I After 33500 nodes, 13171 on tree, 137232.33 best solution, best possible 132042.66 (54.50 seconds)\n", + "Cbc0010I After 33600 nodes, 13218 on tree, 137232.33 best solution, best possible 132047.02 (54.66 seconds)\n", + "Cbc0010I After 33700 nodes, 13265 on tree, 137232.33 best solution, best possible 132051.01 (54.84 seconds)\n", + "Cbc0010I After 33800 nodes, 13315 on tree, 137232.33 best solution, best possible 132054.45 (55.00 seconds)\n", + "Cbc0010I After 33900 nodes, 13365 on tree, 137232.33 best solution, best possible 132057.46 (55.21 seconds)\n", + "Cbc0010I After 34000 nodes, 13410 on tree, 137232.33 best solution, best possible 132061.37 (55.35 seconds)\n", + "Cbc0010I After 34100 nodes, 13462 on tree, 137232.33 best solution, best possible 132061.37 (55.50 seconds)\n", + "Cbc0010I After 34200 nodes, 13503 on tree, 137232.33 best solution, best possible 132061.37 (55.63 seconds)\n", + "Cbc0010I After 34300 nodes, 13548 on tree, 137232.33 best solution, best possible 132061.37 (55.78 seconds)\n", + "Cbc0010I After 34400 nodes, 13594 on tree, 137232.33 best solution, best possible 132061.37 (55.92 seconds)\n", + "Cbc0010I After 34500 nodes, 13633 on tree, 137232.33 best solution, best possible 132061.37 (56.06 seconds)\n", + "Cbc0010I After 34600 nodes, 13675 on tree, 137232.33 best solution, best possible 132061.37 (56.22 seconds)\n", + "Cbc0010I After 34700 nodes, 13720 on tree, 137232.33 best solution, best possible 132061.37 (56.37 seconds)\n", + "Cbc0010I After 34800 nodes, 13759 on tree, 137232.33 best solution, best possible 132061.37 (56.52 seconds)\n", + "Cbc0010I After 34900 nodes, 13808 on tree, 137232.33 best solution, best possible 132061.37 (56.66 seconds)\n", + "Cbc0010I After 35000 nodes, 13853 on tree, 137232.33 best solution, best possible 132061.37 (56.79 seconds)\n", + "Cbc0010I After 35100 nodes, 13899 on tree, 137232.33 best solution, best possible 132061.37 (56.94 seconds)\n", + "Cbc0010I After 35200 nodes, 13949 on tree, 137232.33 best solution, best possible 132061.37 (57.07 seconds)\n", + "Cbc0012I Integer solution of 135149.36 found by heuristic after 1791420 iterations and 35255 nodes (57.13 seconds)\n", + "Cbc0010I After 35300 nodes, 10440 on tree, 135149.36 best solution, best possible 132061.37 (57.24 seconds)\n", + "Cbc0010I After 35400 nodes, 10464 on tree, 135149.36 best solution, best possible 132061.37 (57.35 seconds)\n", + "Cbc0010I After 35500 nodes, 10499 on tree, 135149.36 best solution, best possible 132061.37 (57.49 seconds)\n", + "Cbc0010I After 35600 nodes, 10534 on tree, 135149.36 best solution, best possible 132061.37 (57.67 seconds)\n", + "Cbc0010I After 35700 nodes, 10572 on tree, 135149.36 best solution, best possible 132061.37 (57.80 seconds)\n", + "Cbc0010I After 35800 nodes, 10613 on tree, 135149.36 best solution, best possible 132061.37 (57.96 seconds)\n", + "Cbc0010I After 35900 nodes, 10649 on tree, 135149.36 best solution, best possible 132061.37 (58.12 seconds)\n", + "Cbc0010I After 36000 nodes, 10688 on tree, 135149.36 best solution, best possible 132061.37 (58.30 seconds)\n", + "Cbc0010I After 36100 nodes, 10656 on tree, 135149.36 best solution, best possible 132061.37 (58.37 seconds)\n", + "Cbc0010I After 36200 nodes, 10637 on tree, 135149.36 best solution, best possible 132061.37 (58.42 seconds)\n", + "Cbc0010I After 36300 nodes, 10619 on tree, 135149.36 best solution, best possible 132061.37 (58.48 seconds)\n", + "Cbc0010I After 36400 nodes, 10599 on tree, 135149.36 best solution, best possible 132061.37 (58.55 seconds)\n", + "Cbc0010I After 36500 nodes, 10585 on tree, 135149.36 best solution, best possible 132061.37 (58.62 seconds)\n", + "Cbc0010I After 36600 nodes, 10567 on tree, 135149.36 best solution, best possible 132061.37 (58.70 seconds)\n", + "Cbc0010I After 36700 nodes, 10553 on tree, 135149.36 best solution, best possible 132061.37 (58.77 seconds)\n", + "Cbc0010I After 36800 nodes, 10533 on tree, 135149.36 best solution, best possible 132061.37 (58.86 seconds)\n", + "Cbc0010I After 36900 nodes, 10509 on tree, 135149.36 best solution, best possible 132061.37 (58.95 seconds)\n", + "Cbc0010I After 37000 nodes, 10489 on tree, 135149.36 best solution, best possible 132061.37 (59.03 seconds)\n", + "Cbc0010I After 37100 nodes, 10533 on tree, 135149.36 best solution, best possible 132064.99 (59.34 seconds)\n", + "Cbc0010I After 37200 nodes, 10579 on tree, 135149.36 best solution, best possible 132067.73 (59.52 seconds)\n", + "Cbc0010I After 37300 nodes, 10626 on tree, 135149.36 best solution, best possible 132072.01 (59.70 seconds)\n", + "Cbc0010I After 37400 nodes, 10666 on tree, 135149.36 best solution, best possible 132075.84 (59.89 seconds)\n", + "Cbc0010I After 37500 nodes, 10713 on tree, 135149.36 best solution, best possible 132077.94 (60.07 seconds)\n", + "Cbc0010I After 37600 nodes, 10758 on tree, 135149.36 best solution, best possible 132082.47 (60.24 seconds)\n", + "Cbc0010I After 37700 nodes, 10803 on tree, 135149.36 best solution, best possible 132086.17 (60.41 seconds)\n", + "Cbc0010I After 37800 nodes, 10849 on tree, 135149.36 best solution, best possible 132090.2 (60.56 seconds)\n", + "Cbc0010I After 37900 nodes, 10897 on tree, 135149.36 best solution, best possible 132092.96 (60.72 seconds)\n", + "Cbc0010I After 38000 nodes, 10941 on tree, 135149.36 best solution, best possible 132097.12 (60.90 seconds)\n", + "Cbc0010I After 38100 nodes, 10983 on tree, 135149.36 best solution, best possible 132097.87 (61.05 seconds)\n", + "Cbc0010I After 38200 nodes, 11019 on tree, 135149.36 best solution, best possible 132097.87 (61.24 seconds)\n", + "Cbc0010I After 38300 nodes, 11061 on tree, 135149.36 best solution, best possible 132097.87 (61.37 seconds)\n", + "Cbc0010I After 38400 nodes, 11100 on tree, 135149.36 best solution, best possible 132097.87 (61.53 seconds)\n", + "Cbc0010I After 38500 nodes, 11138 on tree, 135149.36 best solution, best possible 132097.87 (61.71 seconds)\n", + "Cbc0010I After 38600 nodes, 11177 on tree, 135149.36 best solution, best possible 132097.87 (61.86 seconds)\n", + "Cbc0010I After 38700 nodes, 11215 on tree, 135149.36 best solution, best possible 132097.87 (61.98 seconds)\n", + "Cbc0010I After 38800 nodes, 11255 on tree, 135149.36 best solution, best possible 132097.87 (62.10 seconds)\n", + "Cbc0010I After 38900 nodes, 11292 on tree, 135149.36 best solution, best possible 132097.87 (62.26 seconds)\n", + "Cbc0010I After 39000 nodes, 11330 on tree, 135149.36 best solution, best possible 132097.87 (62.42 seconds)\n", + "Cbc0010I After 39100 nodes, 11363 on tree, 135149.36 best solution, best possible 132097.87 (62.57 seconds)\n", + "Cbc0010I After 39200 nodes, 11394 on tree, 135149.36 best solution, best possible 132097.87 (62.71 seconds)\n", + "Cbc0010I After 39300 nodes, 11432 on tree, 135149.36 best solution, best possible 132097.87 (62.87 seconds)\n", + "Cbc0010I After 39400 nodes, 11468 on tree, 135149.36 best solution, best possible 132097.87 (62.99 seconds)\n", + "Cbc0010I After 39500 nodes, 11502 on tree, 135149.36 best solution, best possible 132097.87 (63.13 seconds)\n", + "Cbc0010I After 39600 nodes, 11541 on tree, 135149.36 best solution, best possible 132097.87 (63.28 seconds)\n", + "Cbc0010I After 39700 nodes, 11575 on tree, 135149.36 best solution, best possible 132097.87 (63.43 seconds)\n", + "Cbc0010I After 39800 nodes, 11614 on tree, 135149.36 best solution, best possible 132097.87 (63.59 seconds)\n", + "Cbc0010I After 39900 nodes, 11651 on tree, 135149.36 best solution, best possible 132097.87 (63.73 seconds)\n", + "Cbc0010I After 40000 nodes, 11693 on tree, 135149.36 best solution, best possible 132097.87 (63.90 seconds)\n", + "Cbc0010I After 40100 nodes, 11695 on tree, 135149.36 best solution, best possible 132097.87 (63.97 seconds)\n", + "Cbc0010I After 40200 nodes, 11686 on tree, 135149.36 best solution, best possible 132097.87 (64.03 seconds)\n", + "Cbc0010I After 40300 nodes, 11671 on tree, 135149.36 best solution, best possible 132097.87 (64.10 seconds)\n", + "Cbc0010I After 40400 nodes, 11654 on tree, 135149.36 best solution, best possible 132097.87 (64.17 seconds)\n", + "Cbc0010I After 40500 nodes, 11640 on tree, 135149.36 best solution, best possible 132097.87 (64.25 seconds)\n", + "Cbc0010I After 40600 nodes, 11628 on tree, 135149.36 best solution, best possible 132097.87 (64.32 seconds)\n", + "Cbc0012I Integer solution of 134904.68 found by heuristic after 2051974 iterations and 40681 nodes (64.39 seconds)\n", + "Cbc0010I After 40700 nodes, 11170 on tree, 134904.68 best solution, best possible 132097.87 (64.42 seconds)\n", + "Cbc0010I After 40800 nodes, 11201 on tree, 134904.68 best solution, best possible 132097.87 (64.58 seconds)\n", + "Cbc0010I After 40900 nodes, 11234 on tree, 134904.68 best solution, best possible 132097.87 (64.74 seconds)\n", + "Cbc0010I After 41000 nodes, 11272 on tree, 134904.68 best solution, best possible 132097.87 (64.91 seconds)\n", + "Cbc0010I After 41100 nodes, 11315 on tree, 134904.68 best solution, best possible 132101.29 (65.11 seconds)\n", + "Cbc0010I After 41200 nodes, 11360 on tree, 134904.68 best solution, best possible 132105.16 (65.32 seconds)\n", + "Cbc0010I After 41300 nodes, 11403 on tree, 134904.68 best solution, best possible 132110.01 (65.54 seconds)\n", + "Cbc0010I After 41400 nodes, 11449 on tree, 134904.68 best solution, best possible 132113.07 (65.72 seconds)\n", + "Cbc0010I After 41500 nodes, 11490 on tree, 134904.68 best solution, best possible 132116.64 (65.92 seconds)\n", + "Cbc0010I After 41600 nodes, 11536 on tree, 134904.68 best solution, best possible 132119.72 (66.15 seconds)\n", + "Cbc0010I After 41700 nodes, 11580 on tree, 134904.68 best solution, best possible 132123.91 (66.37 seconds)\n", + "Cbc0010I After 41800 nodes, 11623 on tree, 134904.68 best solution, best possible 132126.8 (66.58 seconds)\n", + "Cbc0010I After 41900 nodes, 11669 on tree, 134904.68 best solution, best possible 132131.9 (66.80 seconds)\n", + "Cbc0010I After 42000 nodes, 11712 on tree, 134904.68 best solution, best possible 132134.65 (67.02 seconds)\n", + "Cbc0010I After 42100 nodes, 11754 on tree, 134904.68 best solution, best possible 132135.4 (67.23 seconds)\n", + "Cbc0010I After 42200 nodes, 11791 on tree, 134904.68 best solution, best possible 132135.4 (67.42 seconds)\n", + "Cbc0010I After 42300 nodes, 11830 on tree, 134904.68 best solution, best possible 132135.4 (67.57 seconds)\n", + "Cbc0010I After 42400 nodes, 11864 on tree, 134904.68 best solution, best possible 132135.4 (67.73 seconds)\n", + "Cbc0010I After 42500 nodes, 11903 on tree, 134904.68 best solution, best possible 132135.4 (67.90 seconds)\n", + "Cbc0010I After 42600 nodes, 11938 on tree, 134904.68 best solution, best possible 132135.4 (68.04 seconds)\n", + "Cbc0010I After 42700 nodes, 11972 on tree, 134904.68 best solution, best possible 132135.4 (68.19 seconds)\n", + "Cbc0010I After 42800 nodes, 12001 on tree, 134904.68 best solution, best possible 132135.4 (68.35 seconds)\n", + "Cbc0010I After 42900 nodes, 12032 on tree, 134904.68 best solution, best possible 132135.4 (68.53 seconds)\n", + "Cbc0010I After 43000 nodes, 12067 on tree, 134904.68 best solution, best possible 132135.4 (68.68 seconds)\n", + "Cbc0010I After 43100 nodes, 12097 on tree, 134904.68 best solution, best possible 132135.4 (68.84 seconds)\n", + "Cbc0010I After 43200 nodes, 12130 on tree, 134904.68 best solution, best possible 132135.4 (69.01 seconds)\n", + "Cbc0010I After 43300 nodes, 12159 on tree, 134904.68 best solution, best possible 132135.4 (69.19 seconds)\n", + "Cbc0010I After 43400 nodes, 12194 on tree, 134904.68 best solution, best possible 132135.4 (69.38 seconds)\n", + "Cbc0010I After 43500 nodes, 12224 on tree, 134904.68 best solution, best possible 132135.4 (69.54 seconds)\n", + "Cbc0010I After 43600 nodes, 12257 on tree, 134904.68 best solution, best possible 132135.4 (69.69 seconds)\n", + "Cbc0010I After 43700 nodes, 12287 on tree, 134904.68 best solution, best possible 132135.4 (69.89 seconds)\n", + "Cbc0010I After 43800 nodes, 12317 on tree, 134904.68 best solution, best possible 132135.4 (70.03 seconds)\n", + "Cbc0010I After 43900 nodes, 12351 on tree, 134904.68 best solution, best possible 132135.4 (70.19 seconds)\n", + "Cbc0010I After 44000 nodes, 12388 on tree, 134904.68 best solution, best possible 132135.4 (70.36 seconds)\n", + "Cbc0010I After 44100 nodes, 12378 on tree, 134904.68 best solution, best possible 132135.4 (70.43 seconds)\n", + "Cbc0010I After 44200 nodes, 12356 on tree, 134904.68 best solution, best possible 132135.4 (70.50 seconds)\n", + "Cbc0010I After 44300 nodes, 12346 on tree, 134904.68 best solution, best possible 132135.4 (70.59 seconds)\n", + "Cbc0010I After 44400 nodes, 12326 on tree, 134904.68 best solution, best possible 132135.4 (70.68 seconds)\n", + "Cbc0010I After 44500 nodes, 12315 on tree, 134904.68 best solution, best possible 132135.4 (70.77 seconds)\n", + "Cbc0010I After 44600 nodes, 12289 on tree, 134904.68 best solution, best possible 132135.4 (70.86 seconds)\n", + "Cbc0010I After 44700 nodes, 12276 on tree, 134904.68 best solution, best possible 132135.4 (70.96 seconds)\n", + "Cbc0010I After 44800 nodes, 12266 on tree, 134904.68 best solution, best possible 132135.4 (71.06 seconds)\n", + "Cbc0010I After 44900 nodes, 12255 on tree, 134904.68 best solution, best possible 132135.4 (71.15 seconds)\n", + "Cbc0010I After 45000 nodes, 12240 on tree, 134904.68 best solution, best possible 132135.4 (71.25 seconds)\n", + "Cbc0010I After 45100 nodes, 12284 on tree, 134904.68 best solution, best possible 132138.5 (71.53 seconds)\n", + "Cbc0010I After 45200 nodes, 12326 on tree, 134904.68 best solution, best possible 132142.24 (71.71 seconds)\n", + "Cbc0010I After 45300 nodes, 12371 on tree, 134904.68 best solution, best possible 132146 (71.91 seconds)\n", + "Cbc0010I After 45400 nodes, 12416 on tree, 134904.68 best solution, best possible 132149.5 (72.12 seconds)\n", + "Cbc0010I After 45500 nodes, 12460 on tree, 134904.68 best solution, best possible 132154.47 (72.32 seconds)\n", + "Cbc0010I After 45600 nodes, 12506 on tree, 134904.68 best solution, best possible 132157.81 (72.49 seconds)\n", + "Cbc0010I After 45700 nodes, 12550 on tree, 134904.68 best solution, best possible 132160.4 (72.70 seconds)\n", + "Cbc0010I After 45800 nodes, 12588 on tree, 134904.68 best solution, best possible 132164.4 (72.89 seconds)\n", + "Cbc0010I After 45900 nodes, 12631 on tree, 134904.68 best solution, best possible 132167.36 (73.09 seconds)\n", + "Cbc0010I After 46000 nodes, 12672 on tree, 134904.68 best solution, best possible 132172.27 (73.28 seconds)\n", + "Cbc0010I After 46100 nodes, 12709 on tree, 134904.68 best solution, best possible 132172.75 (73.42 seconds)\n", + "Cbc0010I After 46200 nodes, 12740 on tree, 134904.68 best solution, best possible 132172.75 (73.59 seconds)\n", + "Cbc0010I After 46300 nodes, 12776 on tree, 134904.68 best solution, best possible 132172.75 (73.74 seconds)\n", + "Cbc0010I After 46400 nodes, 12805 on tree, 134904.68 best solution, best possible 132172.75 (73.89 seconds)\n", + "Cbc0010I After 46500 nodes, 12833 on tree, 134904.68 best solution, best possible 132172.75 (74.06 seconds)\n", + "Cbc0010I After 46600 nodes, 12860 on tree, 134904.68 best solution, best possible 132172.75 (74.21 seconds)\n", + "Cbc0010I After 46700 nodes, 12888 on tree, 134904.68 best solution, best possible 132172.75 (74.34 seconds)\n", + "Cbc0010I After 46800 nodes, 12913 on tree, 134904.68 best solution, best possible 132172.75 (74.47 seconds)\n", + "Cbc0010I After 46900 nodes, 12945 on tree, 134904.68 best solution, best possible 132172.75 (74.64 seconds)\n", + "Cbc0010I After 47000 nodes, 12983 on tree, 134904.68 best solution, best possible 132172.75 (74.81 seconds)\n", + "Cbc0010I After 47100 nodes, 13011 on tree, 134904.68 best solution, best possible 132172.75 (74.98 seconds)\n", + "Cbc0010I After 47200 nodes, 13034 on tree, 134904.68 best solution, best possible 132172.75 (75.11 seconds)\n", + "Cbc0010I After 47300 nodes, 13069 on tree, 134904.68 best solution, best possible 132172.75 (75.25 seconds)\n", + "Cbc0010I After 47400 nodes, 13105 on tree, 134904.68 best solution, best possible 132172.75 (75.39 seconds)\n", + "Cbc0010I After 47500 nodes, 13144 on tree, 134904.68 best solution, best possible 132172.75 (75.54 seconds)\n", + "Cbc0010I After 47600 nodes, 13174 on tree, 134904.68 best solution, best possible 132172.75 (75.68 seconds)\n", + "Cbc0010I After 47700 nodes, 13204 on tree, 134904.68 best solution, best possible 132172.75 (75.81 seconds)\n", + "Cbc0010I After 47800 nodes, 13228 on tree, 134904.68 best solution, best possible 132172.75 (75.95 seconds)\n", + "Cbc0010I After 47900 nodes, 13266 on tree, 134904.68 best solution, best possible 132172.75 (76.12 seconds)\n", + "Cbc0010I After 48000 nodes, 13309 on tree, 134904.68 best solution, best possible 132172.75 (76.28 seconds)\n", + "Cbc0010I After 48100 nodes, 13295 on tree, 134904.68 best solution, best possible 132172.75 (76.35 seconds)\n", + "Cbc0010I After 48200 nodes, 13284 on tree, 134904.68 best solution, best possible 132172.75 (76.40 seconds)\n", + "Cbc0010I After 48300 nodes, 13273 on tree, 134904.68 best solution, best possible 132172.75 (76.48 seconds)\n", + "Cbc0010I After 48400 nodes, 13258 on tree, 134904.68 best solution, best possible 132172.75 (76.57 seconds)\n", + "Cbc0010I After 48500 nodes, 13244 on tree, 134904.68 best solution, best possible 132172.75 (76.65 seconds)\n", + "Cbc0010I After 48600 nodes, 13234 on tree, 134904.68 best solution, best possible 132172.75 (76.74 seconds)\n", + "Cbc0010I After 48700 nodes, 13232 on tree, 134904.68 best solution, best possible 132172.75 (76.82 seconds)\n", + "Cbc0010I After 48800 nodes, 13220 on tree, 134904.68 best solution, best possible 132172.75 (76.91 seconds)\n", + "Cbc0010I After 48900 nodes, 13210 on tree, 134904.68 best solution, best possible 132172.75 (76.98 seconds)\n", + "Cbc0010I After 49000 nodes, 13193 on tree, 134904.68 best solution, best possible 132172.75 (77.06 seconds)\n", + "Cbc0010I After 49100 nodes, 13240 on tree, 134904.68 best solution, best possible 132176.02 (77.26 seconds)\n", + "Cbc0010I After 49200 nodes, 13289 on tree, 134904.68 best solution, best possible 132180.37 (77.45 seconds)\n", + "Cbc0010I After 49300 nodes, 13332 on tree, 134904.68 best solution, best possible 132184.07 (77.64 seconds)\n", + "Cbc0010I After 49400 nodes, 13377 on tree, 134904.68 best solution, best possible 132187.06 (77.81 seconds)\n", + "Cbc0010I After 49500 nodes, 13421 on tree, 134904.68 best solution, best possible 132190.74 (78.00 seconds)\n", + "Cbc0010I After 49600 nodes, 13463 on tree, 134904.68 best solution, best possible 132194.13 (78.20 seconds)\n", + "Cbc0010I After 49700 nodes, 13503 on tree, 134904.68 best solution, best possible 132197.99 (78.38 seconds)\n", + "Cbc0010I After 49800 nodes, 13545 on tree, 134904.68 best solution, best possible 132201.52 (78.54 seconds)\n", + "Cbc0010I After 49900 nodes, 13589 on tree, 134904.68 best solution, best possible 132204.62 (78.73 seconds)\n", + "Cbc0010I After 50000 nodes, 13636 on tree, 134904.68 best solution, best possible 132208.31 (78.93 seconds)\n", + "Cbc0010I After 50100 nodes, 13678 on tree, 134904.68 best solution, best possible 132209.34 (79.09 seconds)\n", + "Cbc0010I After 50200 nodes, 13709 on tree, 134904.68 best solution, best possible 132209.34 (79.28 seconds)\n", + "Cbc0010I After 50300 nodes, 13737 on tree, 134904.68 best solution, best possible 132209.34 (79.44 seconds)\n", + "Cbc0010I After 50400 nodes, 13769 on tree, 134904.68 best solution, best possible 132209.34 (79.58 seconds)\n", + "Cbc0010I After 50500 nodes, 13802 on tree, 134904.68 best solution, best possible 132209.34 (79.74 seconds)\n", + "Cbc0010I After 50600 nodes, 13825 on tree, 134904.68 best solution, best possible 132209.34 (79.87 seconds)\n", + "Cbc0010I After 50700 nodes, 13855 on tree, 134904.68 best solution, best possible 132209.34 (80.03 seconds)\n", + "Cbc0010I After 50800 nodes, 13873 on tree, 134904.68 best solution, best possible 132209.34 (80.17 seconds)\n", + "Cbc0010I After 50900 nodes, 13903 on tree, 134904.68 best solution, best possible 132209.34 (80.31 seconds)\n", + "Cbc0010I After 51000 nodes, 13926 on tree, 134904.68 best solution, best possible 132209.34 (80.45 seconds)\n", + "Cbc0010I After 51100 nodes, 13957 on tree, 134904.68 best solution, best possible 132209.34 (80.60 seconds)\n", + "Cbc0010I After 51200 nodes, 13989 on tree, 134904.68 best solution, best possible 132209.34 (80.74 seconds)\n", + "Cbc0010I After 51300 nodes, 14016 on tree, 134904.68 best solution, best possible 132209.34 (80.87 seconds)\n", + "Cbc0010I After 51400 nodes, 14044 on tree, 134904.68 best solution, best possible 132209.34 (81.03 seconds)\n", + "Cbc0010I After 51500 nodes, 14077 on tree, 134904.68 best solution, best possible 132209.34 (81.17 seconds)\n", + "Cbc0010I After 51600 nodes, 14098 on tree, 134904.68 best solution, best possible 132209.34 (81.34 seconds)\n", + "Cbc0010I After 51700 nodes, 14129 on tree, 134904.68 best solution, best possible 132209.34 (81.46 seconds)\n", + "Cbc0010I After 51800 nodes, 14156 on tree, 134904.68 best solution, best possible 132209.34 (81.61 seconds)\n", + "Cbc0010I After 51900 nodes, 14186 on tree, 134904.68 best solution, best possible 132209.34 (81.75 seconds)\n", + "Cbc0010I After 52000 nodes, 14213 on tree, 134904.68 best solution, best possible 132209.34 (81.91 seconds)\n", + "Cbc0010I After 52100 nodes, 14205 on tree, 134904.68 best solution, best possible 132209.34 (81.98 seconds)\n", + "Cbc0010I After 52200 nodes, 14198 on tree, 134904.68 best solution, best possible 132209.34 (82.04 seconds)\n", + "Cbc0010I After 52300 nodes, 14187 on tree, 134904.68 best solution, best possible 132209.34 (82.11 seconds)\n", + "Cbc0010I After 52400 nodes, 14178 on tree, 134904.68 best solution, best possible 132209.34 (82.20 seconds)\n", + "Cbc0010I After 52500 nodes, 14172 on tree, 134904.68 best solution, best possible 132209.34 (82.28 seconds)\n", + "Cbc0010I After 52600 nodes, 14160 on tree, 134904.68 best solution, best possible 132209.34 (82.36 seconds)\n", + "Cbc0010I After 52700 nodes, 14150 on tree, 134904.68 best solution, best possible 132209.34 (82.45 seconds)\n", + "Cbc0010I After 52800 nodes, 14144 on tree, 134904.68 best solution, best possible 132209.34 (82.53 seconds)\n", + "Cbc0010I After 52900 nodes, 14133 on tree, 134904.68 best solution, best possible 132209.34 (82.64 seconds)\n", + "Cbc0010I After 53000 nodes, 14119 on tree, 134904.68 best solution, best possible 132209.34 (82.72 seconds)\n", + "Cbc0010I After 53100 nodes, 14166 on tree, 134904.68 best solution, best possible 132213.06 (82.97 seconds)\n", + "Cbc0010I After 53200 nodes, 14212 on tree, 134904.68 best solution, best possible 132216.5 (83.18 seconds)\n", + "Cbc0010I After 53300 nodes, 14255 on tree, 134904.68 best solution, best possible 132219.82 (83.36 seconds)\n", + "Cbc0010I After 53400 nodes, 14298 on tree, 134904.68 best solution, best possible 132225.43 (83.56 seconds)\n", + "Cbc0010I After 53500 nodes, 14344 on tree, 134904.68 best solution, best possible 132229.15 (83.73 seconds)\n", + "Cbc0010I After 53600 nodes, 14388 on tree, 134904.68 best solution, best possible 132233.42 (83.93 seconds)\n", + "Cbc0010I After 53700 nodes, 14432 on tree, 134904.68 best solution, best possible 132236.62 (84.12 seconds)\n", + "Cbc0010I After 53800 nodes, 14480 on tree, 134904.68 best solution, best possible 132240.78 (84.29 seconds)\n", + "Cbc0010I After 53900 nodes, 14520 on tree, 134904.68 best solution, best possible 132244.08 (84.49 seconds)\n", + "Cbc0010I After 54000 nodes, 14565 on tree, 134904.68 best solution, best possible 132248.03 (84.71 seconds)\n", + "Cbc0010I After 54100 nodes, 14608 on tree, 134904.68 best solution, best possible 132248.63 (84.85 seconds)\n", + "Cbc0010I After 54200 nodes, 14636 on tree, 134904.68 best solution, best possible 132248.63 (85.02 seconds)\n", + "Cbc0010I After 54300 nodes, 14670 on tree, 134904.68 best solution, best possible 132248.63 (85.18 seconds)\n", + "Cbc0010I After 54400 nodes, 14696 on tree, 134904.68 best solution, best possible 132248.63 (85.32 seconds)\n", + "Cbc0010I After 54500 nodes, 14717 on tree, 134904.68 best solution, best possible 132248.63 (85.45 seconds)\n", + "Cbc0010I After 54600 nodes, 14747 on tree, 134904.68 best solution, best possible 132248.63 (85.63 seconds)\n", + "Cbc0010I After 54700 nodes, 14779 on tree, 134904.68 best solution, best possible 132248.63 (85.77 seconds)\n", + "Cbc0010I After 54800 nodes, 14798 on tree, 134904.68 best solution, best possible 132248.63 (85.88 seconds)\n", + "Cbc0010I After 54900 nodes, 14828 on tree, 134904.68 best solution, best possible 132248.63 (86.03 seconds)\n", + "Cbc0010I After 55000 nodes, 14854 on tree, 134904.68 best solution, best possible 132248.63 (86.19 seconds)\n", + "Cbc0010I After 55100 nodes, 14878 on tree, 134904.68 best solution, best possible 132248.63 (86.34 seconds)\n", + "Cbc0010I After 55200 nodes, 14911 on tree, 134904.68 best solution, best possible 132248.63 (86.52 seconds)\n", + "Cbc0010I After 55300 nodes, 14934 on tree, 134904.68 best solution, best possible 132248.63 (86.67 seconds)\n", + "Cbc0010I After 55400 nodes, 14970 on tree, 134904.68 best solution, best possible 132248.63 (86.81 seconds)\n", + "Cbc0010I After 55500 nodes, 15000 on tree, 134904.68 best solution, best possible 132248.63 (86.96 seconds)\n", + "Cbc0010I After 55600 nodes, 15028 on tree, 134904.68 best solution, best possible 132248.63 (87.10 seconds)\n", + "Cbc0010I After 55700 nodes, 15056 on tree, 134904.68 best solution, best possible 132248.63 (87.27 seconds)\n", + "Cbc0010I After 55800 nodes, 15082 on tree, 134904.68 best solution, best possible 132248.63 (87.43 seconds)\n", + "Cbc0010I After 55900 nodes, 15114 on tree, 134904.68 best solution, best possible 132248.63 (87.60 seconds)\n", + "Cbc0010I After 56000 nodes, 15139 on tree, 134904.68 best solution, best possible 132248.63 (87.76 seconds)\n", + "Cbc0010I After 56100 nodes, 15132 on tree, 134904.68 best solution, best possible 132248.63 (87.83 seconds)\n", + "Cbc0010I After 56200 nodes, 15125 on tree, 134904.68 best solution, best possible 132248.63 (87.90 seconds)\n", + "Cbc0010I After 56300 nodes, 15118 on tree, 134904.68 best solution, best possible 132248.63 (87.98 seconds)\n", + "Cbc0010I After 56400 nodes, 15114 on tree, 134904.68 best solution, best possible 132248.63 (88.05 seconds)\n", + "Cbc0010I After 56500 nodes, 15095 on tree, 134904.68 best solution, best possible 132248.63 (88.12 seconds)\n", + "Cbc0010I After 56600 nodes, 15084 on tree, 134904.68 best solution, best possible 132248.63 (88.22 seconds)\n", + "Cbc0010I After 56700 nodes, 15071 on tree, 134904.68 best solution, best possible 132248.63 (88.30 seconds)\n", + "Cbc0010I After 56800 nodes, 15060 on tree, 134904.68 best solution, best possible 132248.63 (88.38 seconds)\n", + "Cbc0010I After 56900 nodes, 15056 on tree, 134904.68 best solution, best possible 132248.63 (88.49 seconds)\n", + "Cbc0010I After 57000 nodes, 15041 on tree, 134904.68 best solution, best possible 132248.63 (88.58 seconds)\n", + "Cbc0010I After 57100 nodes, 15085 on tree, 134904.68 best solution, best possible 132251.42 (88.82 seconds)\n", + "Cbc0010I After 57200 nodes, 15128 on tree, 134904.68 best solution, best possible 132255.58 (89.02 seconds)\n", + "Cbc0010I After 57300 nodes, 15171 on tree, 134904.68 best solution, best possible 132259.22 (89.25 seconds)\n", + "Cbc0010I After 57400 nodes, 15217 on tree, 134904.68 best solution, best possible 132263.19 (89.47 seconds)\n", + "Cbc0010I After 57500 nodes, 15259 on tree, 134904.68 best solution, best possible 132267.73 (89.68 seconds)\n", + "Cbc0010I After 57600 nodes, 15302 on tree, 134904.68 best solution, best possible 132270.57 (89.87 seconds)\n", + "Cbc0010I After 57700 nodes, 15345 on tree, 134904.68 best solution, best possible 132275.24 (90.04 seconds)\n", + "Cbc0010I After 57800 nodes, 15390 on tree, 134904.68 best solution, best possible 132279.31 (90.21 seconds)\n", + "Cbc0010I After 57900 nodes, 15435 on tree, 134904.68 best solution, best possible 132282.3 (90.39 seconds)\n", + "Cbc0010I After 58000 nodes, 15475 on tree, 134904.68 best solution, best possible 132285.97 (90.56 seconds)\n", + "Cbc0010I After 58100 nodes, 15513 on tree, 134904.68 best solution, best possible 132285.97 (90.73 seconds)\n", + "Cbc0010I After 58200 nodes, 15539 on tree, 134904.68 best solution, best possible 132285.97 (90.87 seconds)\n", + "Cbc0010I After 58300 nodes, 15566 on tree, 134904.68 best solution, best possible 132285.97 (90.99 seconds)\n", + "Cbc0010I After 58400 nodes, 15595 on tree, 134904.68 best solution, best possible 132285.97 (91.14 seconds)\n", + "Cbc0010I After 58500 nodes, 15621 on tree, 134904.68 best solution, best possible 132285.97 (91.27 seconds)\n", + "Cbc0010I After 58600 nodes, 15646 on tree, 134904.68 best solution, best possible 132285.97 (91.42 seconds)\n", + "Cbc0010I After 58700 nodes, 15680 on tree, 134904.68 best solution, best possible 132285.97 (91.56 seconds)\n", + "Cbc0010I After 58800 nodes, 15706 on tree, 134904.68 best solution, best possible 132285.97 (91.69 seconds)\n", + "Cbc0010I After 58900 nodes, 15731 on tree, 134904.68 best solution, best possible 132285.97 (91.84 seconds)\n", + "Cbc0010I After 59000 nodes, 15762 on tree, 134904.68 best solution, best possible 132285.97 (91.95 seconds)\n", + "Cbc0010I After 59100 nodes, 15783 on tree, 134904.68 best solution, best possible 132285.97 (92.06 seconds)\n", + "Cbc0010I After 59200 nodes, 15808 on tree, 134904.68 best solution, best possible 132285.97 (92.19 seconds)\n", + "Cbc0010I After 59300 nodes, 15835 on tree, 134904.68 best solution, best possible 132285.97 (92.36 seconds)\n", + "Cbc0010I After 59400 nodes, 15864 on tree, 134904.68 best solution, best possible 132285.97 (92.54 seconds)\n", + "Cbc0010I After 59500 nodes, 15893 on tree, 134904.68 best solution, best possible 132285.97 (92.68 seconds)\n", + "Cbc0010I After 59600 nodes, 15917 on tree, 134904.68 best solution, best possible 132285.97 (92.86 seconds)\n", + "Cbc0010I After 59700 nodes, 15945 on tree, 134904.68 best solution, best possible 132285.97 (92.97 seconds)\n", + "Cbc0010I After 59800 nodes, 15972 on tree, 134904.68 best solution, best possible 132285.97 (93.10 seconds)\n", + "Cbc0010I After 59900 nodes, 16009 on tree, 134904.68 best solution, best possible 132285.97 (93.33 seconds)\n", + "Cbc0010I After 60000 nodes, 16033 on tree, 134904.68 best solution, best possible 132285.97 (93.48 seconds)\n", + "Cbc0010I After 60100 nodes, 16019 on tree, 134904.68 best solution, best possible 132285.97 (93.55 seconds)\n", + "Cbc0010I After 60200 nodes, 16010 on tree, 134904.68 best solution, best possible 132285.97 (93.63 seconds)\n", + "Cbc0010I After 60300 nodes, 16005 on tree, 134904.68 best solution, best possible 132285.97 (93.72 seconds)\n", + "Cbc0010I After 60400 nodes, 15990 on tree, 134904.68 best solution, best possible 132285.97 (93.81 seconds)\n", + "Cbc0010I After 60500 nodes, 15977 on tree, 134904.68 best solution, best possible 132285.97 (93.89 seconds)\n", + "Cbc0010I After 60600 nodes, 15965 on tree, 134904.68 best solution, best possible 132285.97 (93.97 seconds)\n", + "Cbc0010I After 60700 nodes, 15954 on tree, 134904.68 best solution, best possible 132285.97 (94.07 seconds)\n", + "Cbc0010I After 60800 nodes, 15951 on tree, 134904.68 best solution, best possible 132285.97 (94.15 seconds)\n", + "Cbc0010I After 60900 nodes, 15944 on tree, 134904.68 best solution, best possible 132285.97 (94.23 seconds)\n", + "Cbc0010I After 61000 nodes, 15936 on tree, 134904.68 best solution, best possible 132285.97 (94.34 seconds)\n", + "Cbc0010I After 61100 nodes, 15975 on tree, 134904.68 best solution, best possible 132289.61 (94.55 seconds)\n", + "Cbc0010I After 61200 nodes, 16019 on tree, 134904.68 best solution, best possible 132294.17 (94.76 seconds)\n", + "Cbc0010I After 61300 nodes, 16062 on tree, 134904.68 best solution, best possible 132298.06 (94.97 seconds)\n", + "Cbc0010I After 61400 nodes, 16107 on tree, 134904.68 best solution, best possible 132301.8 (95.20 seconds)\n", + "Cbc0010I After 61500 nodes, 16144 on tree, 134904.68 best solution, best possible 132306.42 (95.40 seconds)\n", + "Cbc0010I After 61600 nodes, 16182 on tree, 134904.68 best solution, best possible 132309.81 (95.64 seconds)\n", + "Cbc0010I After 61700 nodes, 16223 on tree, 134904.68 best solution, best possible 132312.66 (95.83 seconds)\n", + "Cbc0010I After 61800 nodes, 16269 on tree, 134904.68 best solution, best possible 132316.38 (96.03 seconds)\n", + "Cbc0010I After 61900 nodes, 16312 on tree, 134904.68 best solution, best possible 132318.89 (96.24 seconds)\n", + "Cbc0010I After 62000 nodes, 16358 on tree, 134904.68 best solution, best possible 132322.73 (96.41 seconds)\n", + "Cbc0010I After 62100 nodes, 16391 on tree, 134904.68 best solution, best possible 132323.88 (96.58 seconds)\n", + "Cbc0010I After 62200 nodes, 16424 on tree, 134904.68 best solution, best possible 132323.88 (96.76 seconds)\n", + "Cbc0010I After 62300 nodes, 16453 on tree, 134904.68 best solution, best possible 132323.88 (96.91 seconds)\n", + "Cbc0010I After 62400 nodes, 16483 on tree, 134904.68 best solution, best possible 132323.88 (97.05 seconds)\n", + "Cbc0010I After 62500 nodes, 16510 on tree, 134904.68 best solution, best possible 132323.88 (97.19 seconds)\n", + "Cbc0010I After 62600 nodes, 16541 on tree, 134904.68 best solution, best possible 132323.88 (97.33 seconds)\n", + "Cbc0010I After 62700 nodes, 16560 on tree, 134904.68 best solution, best possible 132323.88 (97.50 seconds)\n", + "Cbc0010I After 62800 nodes, 16590 on tree, 134904.68 best solution, best possible 132323.88 (97.65 seconds)\n", + "Cbc0010I After 62900 nodes, 16609 on tree, 134904.68 best solution, best possible 132323.88 (97.79 seconds)\n", + "Cbc0010I After 63000 nodes, 16636 on tree, 134904.68 best solution, best possible 132323.88 (97.95 seconds)\n", + "Cbc0010I After 63100 nodes, 16655 on tree, 134904.68 best solution, best possible 132323.88 (98.12 seconds)\n", + "Cbc0010I After 63200 nodes, 16686 on tree, 134904.68 best solution, best possible 132323.88 (98.28 seconds)\n", + "Cbc0010I After 63300 nodes, 16716 on tree, 134904.68 best solution, best possible 132323.88 (98.45 seconds)\n", + "Cbc0010I After 63400 nodes, 16740 on tree, 134904.68 best solution, best possible 132323.88 (98.62 seconds)\n", + "Cbc0010I After 63500 nodes, 16764 on tree, 134904.68 best solution, best possible 132323.88 (98.77 seconds)\n", + "Cbc0010I After 63600 nodes, 16789 on tree, 134904.68 best solution, best possible 132323.88 (98.91 seconds)\n", + "Cbc0010I After 63700 nodes, 16816 on tree, 134904.68 best solution, best possible 132323.88 (99.05 seconds)\n", + "Cbc0010I After 63800 nodes, 16832 on tree, 134904.68 best solution, best possible 132323.88 (99.20 seconds)\n", + "Cbc0010I After 63900 nodes, 16861 on tree, 134904.68 best solution, best possible 132323.88 (99.37 seconds)\n", + "Cbc0010I After 64000 nodes, 16886 on tree, 134904.68 best solution, best possible 132323.88 (99.51 seconds)\n", + "Cbc0010I After 64100 nodes, 16876 on tree, 134904.68 best solution, best possible 132323.88 (99.58 seconds)\n", + "Cbc0010I After 64200 nodes, 16871 on tree, 134904.68 best solution, best possible 132323.88 (99.66 seconds)\n", + "Cbc0010I After 64300 nodes, 16866 on tree, 134904.68 best solution, best possible 132323.88 (99.74 seconds)\n", + "Cbc0010I After 64400 nodes, 16851 on tree, 134904.68 best solution, best possible 132323.88 (99.82 seconds)\n", + "Cbc0010I After 64500 nodes, 16842 on tree, 134904.68 best solution, best possible 132323.88 (99.90 seconds)\n", + "Cbc0010I After 64600 nodes, 16824 on tree, 134904.68 best solution, best possible 132323.88 (99.99 seconds)\n", + "Cbc0010I After 64700 nodes, 16814 on tree, 134904.68 best solution, best possible 132323.88 (100.07 seconds)\n", + "Cbc0010I After 64800 nodes, 16800 on tree, 134904.68 best solution, best possible 132323.88 (100.16 seconds)\n", + "Cbc0010I After 64900 nodes, 16802 on tree, 134904.68 best solution, best possible 132323.88 (100.27 seconds)\n", + "Cbc0010I After 65000 nodes, 16793 on tree, 134904.68 best solution, best possible 132323.88 (100.35 seconds)\n", + "Cbc0010I After 65100 nodes, 16830 on tree, 134904.68 best solution, best possible 132327.41 (100.55 seconds)\n", + "Cbc0010I After 65200 nodes, 16872 on tree, 134904.68 best solution, best possible 132330.97 (100.76 seconds)\n", + "Cbc0010I After 65300 nodes, 16918 on tree, 134904.68 best solution, best possible 132333.8 (100.95 seconds)\n", + "Cbc0010I After 65400 nodes, 16963 on tree, 134904.68 best solution, best possible 132337.96 (101.18 seconds)\n", + "Cbc0010I After 65500 nodes, 17009 on tree, 134904.68 best solution, best possible 132341.56 (101.37 seconds)\n", + "Cbc0010I After 65600 nodes, 17052 on tree, 134904.68 best solution, best possible 132346.47 (101.58 seconds)\n", + "Cbc0010I After 65700 nodes, 17092 on tree, 134904.68 best solution, best possible 132350.34 (101.78 seconds)\n", + "Cbc0010I After 65800 nodes, 17133 on tree, 134904.68 best solution, best possible 132353.66 (101.94 seconds)\n", + "Cbc0010I After 65900 nodes, 17180 on tree, 134904.68 best solution, best possible 132356.33 (102.14 seconds)\n", + "Cbc0010I After 66000 nodes, 17221 on tree, 134904.68 best solution, best possible 132360.58 (102.34 seconds)\n", + "Cbc0010I After 66100 nodes, 17259 on tree, 134904.68 best solution, best possible 132361.27 (102.50 seconds)\n", + "Cbc0010I After 66200 nodes, 17289 on tree, 134904.68 best solution, best possible 132361.27 (102.67 seconds)\n", + "Cbc0010I After 66300 nodes, 17315 on tree, 134904.68 best solution, best possible 132361.27 (102.83 seconds)\n", + "Cbc0010I After 66400 nodes, 17339 on tree, 134904.68 best solution, best possible 132361.27 (103.00 seconds)\n", + "Cbc0030I Thread 0 used 8420 times, waiting to start 0.93927908, 50651 locks, 1.1969278 locked, 0.27505636 waiting for locks\n", + "Cbc0030I Thread 1 used 8403 times, waiting to start 1.0630851, 50619 locks, 1.1164515 locked, 0.27405047 waiting for locks\n", + "Cbc0030I Thread 2 used 8374 times, waiting to start 1.191205, 50354 locks, 1.1643543 locked, 0.32620454 waiting for locks\n", + "Cbc0030I Thread 3 used 8309 times, waiting to start 1.3499148, 50092 locks, 1.1330423 locked, 0.28240466 waiting for locks\n", + "Cbc0030I Thread 4 used 8148 times, waiting to start 1.3096657, 49235 locks, 1.1627154 locked, 0.30409932 waiting for locks\n", + "Cbc0030I Thread 5 used 8225 times, waiting to start 1.3548653, 49550 locks, 1.1332092 locked, 0.28733015 waiting for locks\n", + "Cbc0030I Thread 6 used 8321 times, waiting to start 1.5633504, 49928 locks, 1.1238291 locked, 0.20798039 waiting for locks\n", + "Cbc0030I Thread 7 used 8237 times, waiting to start 1.4876814, 49610 locks, 1.068845 locked, 0.19170403 waiting for locks\n", + "Cbc0030I Main thread 96.825313 waiting for threads, 133626 locks, 0.27769566 locked, 0.61545086 waiting for locks\n", + "Cbc0027I Exiting on user event\n", + "Cbc0005I Partial search - best objective 134904.68 (best possible 132361.27), took 3353649 iterations and 66430 nodes (104.09 seconds)\n", + "Cbc0032I Strong branching done 60924 times (1440093 iterations), fathomed 947 nodes and fixed 7520 variables\n", + "Cbc0035I Maximum depth 60, 1743750 variables fixed on reduced cost\n", + "Cuts at root node changed objective from 127486 to 129783\n", + "Probing was tried 108 times and created 9 cuts of which 0 were active after adding rounds of cuts (0.052 seconds)\n", + "Gomory was tried 25737 times and created 22979 cuts of which 0 were active after adding rounds of cuts (69.307 seconds)\n", + "Knapsack was tried 108 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.021 seconds)\n", + "Clique was tried 108 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)\n", + "MixedIntegerRounding2 was tried 108 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.031 seconds)\n", + "FlowCover was tried 25738 times and created 120456 cuts of which 0 were active after adding rounds of cuts (23.630 seconds)\n", + "TwoMirCuts was tried 25737 times and created 11326 cuts of which 0 were active after adding rounds of cuts (36.000 seconds)\n", + "ZeroHalf was tried 108 times and created 126 cuts of which 0 were active after adding rounds of cuts (0.075 seconds)\n", + "\n", + "Result - User ctrl-cuser ctrl-c\n", + "\n", + "Objective value: 134904.67801955\n", + "Lower bound: 132361.273\n", + "Gap: 0.02\n", + "Enumerated nodes: 66430\n", + "Total iterations: 3353649\n", + "Time (CPU seconds): 104.14\n", + "Time (Wallclock seconds): 104.14\n", + "\n", + "Total time (CPU seconds): 104.19 (Wallclock seconds): 104.19\n", + "\n", + "WARNING: Loading a SolverResults object with an 'aborted' status, but\n", + "containing a solution\n" + ] + } + ], + "source": [ + "cost, state = wfn.design()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "8db2a408-c01a-407b-a68e-502b582a4aeb", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:34:41.284302Z", + "iopub.status.busy": "2024-02-06T13:34:41.284302Z", + "iopub.status.idle": "2024-02-06T13:34:41.559578Z", + "shell.execute_reply": "2024-02-06T13:34:41.559578Z", + "shell.execute_reply.started": "2024-02-06T13:34:41.284302Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "<Axes: >" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "wfn.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "5cdc72d1-55ed-42fb-88f7-b0dee6b76699", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:34:55.056623Z", + "iopub.status.busy": "2024-02-06T13:34:55.055618Z", + "iopub.status.idle": "2024-02-06T13:34:55.072622Z", + "shell.execute_reply": "2024-02-06T13:34:55.071622Z", + "shell.execute_reply.started": "2024-02-06T13:34:55.056623Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total cable length: 134905 m\n" + ] + } + ], + "source": [ + "print(f'Total cable length: {round(wfn.tree_as_table().cable_length.sum())} m')" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "713418fe-fc26-4c28-b498-db8dd25a6cd4", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:34:57.220545Z", + "iopub.status.busy": "2024-02-06T13:34:57.219544Z", + "iopub.status.idle": "2024-02-06T13:34:57.230395Z", + "shell.execute_reply": "2024-02-06T13:34:57.229400Z", + "shell.execute_reply.started": "2024-02-06T13:34:57.220545Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total cost: 43229157 €\n" + ] + } + ], + "source": [ + "print(f'Total cost: {round(cost)} €')" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "842184bd-1079-4a76-a59c-9ae043e8e076", + "metadata": { + "execution": { + "iopub.execute_input": "2024-02-06T13:35:01.728793Z", + "iopub.status.busy": "2024-02-06T13:35:01.728793Z", + "iopub.status.idle": "2024-02-06T13:35:01.746793Z", + "shell.execute_reply": "2024-02-06T13:35:01.745793Z", + "shell.execute_reply.started": "2024-02-06T13:35:01.728793Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>from_node</th>\n", + " <th>to_node</th>\n", + " <th>cable_length</th>\n", + " <th>cable_type</th>\n", + " <th>cable_cost</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>0</th>\n", + " <td>2</td>\n", + " <td>52</td>\n", + " <td>932.197719</td>\n", + " <td>0</td>\n", + " <td>1.920327e+05</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1</th>\n", + " <td>3</td>\n", + " <td>36</td>\n", + " <td>1319.163176</td>\n", + " <td>0</td>\n", + " <td>2.717476e+05</td>\n", + " </tr>\n", + " <tr>\n", + " <th>2</th>\n", + " <td>4</td>\n", + " <td>10</td>\n", + " <td>891.436456</td>\n", + " <td>1</td>\n", + " <td>2.558423e+05</td>\n", + " </tr>\n", + " <tr>\n", + " <th>3</th>\n", + " <td>4</td>\n", + " <td>9</td>\n", + " <td>854.031807</td>\n", + " <td>0</td>\n", + " <td>1.759306e+05</td>\n", + " </tr>\n", + " <tr>\n", + " <th>4</th>\n", + " <td>5</td>\n", + " <td>14</td>\n", + " <td>1046.526294</td>\n", + " <td>1</td>\n", + " <td>3.003530e+05</td>\n", + " </tr>\n", + " <tr>\n", + " <th>...</th>\n", + " <td>...</td>\n", + " <td>...</td>\n", + " <td>...</td>\n", + " <td>...</td>\n", + " <td>...</td>\n", + " </tr>\n", + " <tr>\n", + " <th>69</th>\n", + " <td>66</td>\n", + " <td>73</td>\n", + " <td>1624.954649</td>\n", + " <td>2</td>\n", + " <td>6.597316e+05</td>\n", + " </tr>\n", + " <tr>\n", + " <th>70</th>\n", + " <td>69</td>\n", + " <td>72</td>\n", + " <td>2609.444048</td>\n", + " <td>2</td>\n", + " <td>1.059434e+06</td>\n", + " </tr>\n", + " <tr>\n", + " <th>71</th>\n", + " <td>70</td>\n", + " <td>75</td>\n", + " <td>1940.993913</td>\n", + " <td>1</td>\n", + " <td>5.570653e+05</td>\n", + " </tr>\n", + " <tr>\n", + " <th>72</th>\n", + " <td>72</td>\n", + " <td>1</td>\n", + " <td>6872.517673</td>\n", + " <td>2</td>\n", + " <td>2.790242e+06</td>\n", + " </tr>\n", + " <tr>\n", + " <th>73</th>\n", + " <td>73</td>\n", + " <td>1</td>\n", + " <td>1862.191017</td>\n", + " <td>2</td>\n", + " <td>7.560496e+05</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "<p>74 rows × 5 columns</p>\n", + "</div>" + ], + "text/plain": [ + " from_node to_node cable_length cable_type cable_cost\n", + "0 2 52 932.197719 0 1.920327e+05\n", + "1 3 36 1319.163176 0 2.717476e+05\n", + "2 4 10 891.436456 1 2.558423e+05\n", + "3 4 9 854.031807 0 1.759306e+05\n", + "4 5 14 1046.526294 1 3.003530e+05\n", + ".. ... ... ... ... ...\n", + "69 66 73 1624.954649 2 6.597316e+05\n", + "70 69 72 2609.444048 2 1.059434e+06\n", + "71 70 75 1940.993913 1 5.570653e+05\n", + "72 72 1 6872.517673 2 2.790242e+06\n", + "73 73 1 1862.191017 2 7.560496e+05\n", + "\n", + "[74 rows x 5 columns]" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "wfn.tree_as_table()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "«work»", + "language": "python", + "name": "work" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ed_win/drivers/__init__.py b/ed_win/drivers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ed_win/drivers/drivers_api.py b/ed_win/drivers/drivers_api.py new file mode 100644 index 0000000..a429b81 --- /dev/null +++ b/ed_win/drivers/drivers_api.py @@ -0,0 +1,121 @@ +from pyomo import environ as pyo +import numpy as np +from .interarray.plotting import gplot +from .interarray.svg import svgplot +from .interarray.geometric import delaunay, make_graph_metrics +from .interarray.interarraylib import calcload, G_from_site +from .interarray.pathfinding import PathFinder +from .interarray.interface import assign_cables +from .interarray.heuristics import CPEW, OBEW + + +class InterArray: + def __init__(self, G, solver_name='heuristic(cpew)', gap=0, timelimit=600, other_settings={}, cables=[]): + make_graph_metrics(G) + self.G = G + self.solver_name = solver_name + self.gap = gap + self.timelimit = timelimit + self.other_settings = other_settings + cables_list = cables.tolist() + self.cables = [(cx, capacity, cost) for cx, capacity, cost in cables_list] + self.max_cap = max(cable[1] for cable in self.cables) if self.cables else 0 + + # Run and get the result + self.H = self.run(G) + + def run(self, G): + solver = self._initialize_solver() + if self.solver_name.startswith('heuristic'): + return self._run_heuristic_solver() + else: + return self._run_optimization_solver(solver) + + def _initialize_solver(self): + solver_name = self.solver_name + other_settings = self.other_settings + gap = self.gap + timelimit = self.timelimit + + if solver_name == 'ortools(cp-sat)': + from .interarray.MILP.ortools import cp_model + solver = cp_model.CpSolver() + solver.parameters.relative_gap_limit = gap + solver.parameters.max_time_in_seconds = timelimit + if other_settings.get('tee', False): + solver.parameters.log_search_progress = True + solver.log_callback = print + elif solver_name == 'cplex': + solver = pyo.SolverFactory(solver_name, solver_io='python') + solver.options = {'mipgap': gap, 'timelimit': timelimit} + elif solver_name == 'cbc': + cbc_solver_path = other_settings.get('cbc_solver_path') + if cbc_solver_path: + solver = pyo.SolverFactory(solver_name, executable=cbc_solver_path) + else: + solver = pyo.SolverFactory(solver_name) + solver.options = {'ratioGap': gap, 'seconds': timelimit, 'timeMode': 'elapsed', + 'threads': other_settings.get('threads', 1)} + elif solver_name == 'heuristic(cpew)' or 'heuristic(obew)': + self.solver_name = solver_name + solver = None + else: + print(f'Warning: The given solver name ({solver_name}) is not among the options. ' + f'It will be solved by the default solver (heuristic(cpew))') + self.solver_name = 'heuristic(cpew)' + solver = None + + return solver + + def _run_heuristic_solver(self): + solver_name = self.solver_name + G = self.G + max_cap = self.max_cap + if solver_name == 'heuristic(cpew)': + heuristic_solver = CPEW + elif solver_name == 'heuristic(obew)': + heuristic_solver = OBEW + + print(f'Solving with {solver_name}\n') + H = heuristic_solver(G, max_cap) + calcload(H) + assign_cables(H, self.cables) + self.H = H + return H + + def _run_optimization_solver(self, solver): + G = self.G + max_cap = self.max_cap + other_settings = self.other_settings + if self.solver_name == 'ortools(cp-sat)': + from .interarray.MILP.ortools import make_MILP_length, MILP_solution_to_G, MILP_warmstart_from_G, cp_model + else: + from .interarray.MILP.pyomo import make_MILP_length, MILP_solution_to_G, MILP_warmstart_from_G + A = delaunay(G) + m = make_MILP_length(A, max_cap, + gateXings_constraint=other_settings.get('gateXings_constraint', False), + branching=other_settings.get('branching', False), + gates_limit=other_settings.get('gates_limit', 0)) + + if other_settings.get('warmstart', False): + G_prime = CPEW(G, max_cap) if other_settings.get('gateXings_constraint', False) else OBEW(G, max_cap) + calcload(G_prime) + MILP_warmstart_from_G(m, G_prime) + + print(f'Solving with {self.solver_name}\n') + if self.solver_name == 'ortools(cp-sat)': + status = solver.Solve(m) + print(solver.ResponseStats(), + f"\nbest solution's strategy: {solver.SolutionInfo()}", + f'\ngap: {100 * (solver.ObjectiveValue() - solver.BestObjectiveBound()) / solver.BestObjectiveBound():.1f}%') + else: + status = solver.solve(m, tee=other_settings.get('tee', False), warmstart=other_settings.get('warmstart', False)) + + H = MILP_solution_to_G(m, solver=solver, A=A) + + if not other_settings.get('gateXings_constraint', False): + H = PathFinder(H).create_detours(in_place=True) + calcload(H) + + assign_cables(H, self.cables) + return H diff --git a/ed_win/drivers/ga/CostFunction.py b/ed_win/drivers/ga/CostFunction.py new file mode 100644 index 0000000..d31526c --- /dev/null +++ b/ed_win/drivers/ga/CostFunction.py @@ -0,0 +1,232 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Feb 12 12:19:39 2020 + +@author: juru +""" + +import numpy as np +import networkx as nx + +from .TwoLinesIntersecting import two_lines_intersecting + + +def cost_function(pop, W, WindFarm, Penalization, cables, n_wt, CoordX, CoordY): + + connected_status = False # Constraints 1 + edges_count_status = False # Constraints 2 + capacity_status = False # Constraints 3 + non_crossing_status = False # Constraints 4 + feeders_status = False # Constraints 5 + + G = nx.Graph() +# pop = x['Position'] + edges = W[pop][:, 0:2] + G.add_nodes_from([x + 1 for x in range(n_wt)]) + G.add_edges_from([tuple(edge) for edge in edges]) +# + # nx.draw_networkx(G, with_labels = True, node_color ='green') + connected_status = nx.is_connected(G) + edges_count = G.number_of_edges() + edges_count_status = n_wt - 1 == edges_count + # nx.draw_networkx(G, with_labels = True, node_color ='green') + # =max(cables.Capacity) + T = np.array([x for x in nx.dfs_edges(G, source=1)]).astype(int) + z = (W[pop, -1]).sum() * max(cables.Price) + + if not connected_status: + # print('connected_status: ', connected_status) + z += Penalization.ConnectedComponents + + if not edges_count_status: + # print('edges_count_status: ', edges_count_status) + z += Penalization.EdgesCount + + if connected_status and edges_count_status: + accumulator = np.zeros(T.shape[0]) + for j in range(n_wt - 1): + k = j + 2 + continue_ite = 1 + look_up = k + while continue_ite: + # aux = T[T[:,0]==look_up] + accumulator += (T[:, 1] == look_up).astype(int) + if (T[:, 1] == look_up).astype(int).sum() > 1: + print('Error') +# try: + if T[(T[:, 1] == look_up)][0, 0] == 1: + continue_ite = 0 +# except: +# print(T) +# print(look_up) + else: + look_up = T[(T[:, 1] == look_up)][0, 0] + capacity_status = (accumulator[T[:, 0] == 1] <= cables.MaxCap).all() + if not capacity_status: + z += Penalization.NodesFeeder * (np.max(accumulator[T[:, 0] == 1]) - cables.MaxCap) + if connected_status and edges_count_status and capacity_status: + # %% ASSIGN: (i) LENGTH TO EACH ACTIVE EDGE (ii) CABLE TYPE TO EACH ACTIVE EDGE (iii) COST TO EACH ACTIVE EDGE + T = np.append(T, np.zeros((accumulator.shape[0], 3)), axis=1) + for k in range(T.shape[0]): + aux1 = np.argwhere((W[:, 0] == T[k, 0]) & (W[:, 1] == T[k, 1])) + aux2 = np.argwhere((W[:, 1] == T[k, 0]) & (W[:, 0] == T[k, 1])) + if aux2.size == 0: + T[k, 2] = W[aux1, 2] + if aux1.size == 0: + T[k, 2] = W[aux2, 2] + for k in range(accumulator.shape[0]): + for l in range(cables.Capacity.shape[0]): + if accumulator[k] <= cables.Capacity[l]: + break + T[k, 3] = l + for k in range(T.shape[0]): + T[k, 4] = T[k, 2] * cables.Price[T.astype(int)[k, 3]] + # %% LINES CROSSING OUTER ROUTINE EMBEDDED WITH INNER ROUTINE + # plt.figure(0) + N1 = np.vstack((CoordX[edges.astype(int)[:, 0] - 1], CoordY[edges.astype(int)[:, 0] - 1])).T + N2 = np.vstack((CoordX[edges.astype(int)[:, 1] - 1], CoordY[edges.astype(int)[:, 1] - 1])).T + checker = 0 + for k in range(N1.shape[0]): + for l in range(N2.shape[0] - k - 1): + # print(k) + # print(k+l+1) + line1 = np.array([N1[k], N2[k]]) + line2 = np.array([N1[k + l + 1], N2[k + l + 1]]) + # x1 = [line1[0][0],line1[1][0]] + # y1 = [line1[0][1],line1[1][1]] + # plt.plot(x1, y1, label = "line 1") + # x2 = [line2[0][0],line2[1][0]] + # y2 = [line2[0][1],line2[1][1]] + # plt.plot(x2, y2, label = "line 2") + checker += two_lines_intersecting(line1, line2) + if checker == 0: + non_crossing_status = True + # %% DETERMINE NUMBER OF MAIN FEEDERS + excess_feeders = 0 + feeders_status = sum(T[:, 0] == 1) <= WindFarm.Feeders + if not feeders_status: + excess_feeders = sum(T[:, 0] == 1) - WindFarm.Feeders + # %% CALCULATE ECONOMIC VALUE OF THE SOLUTION GIVEN THE PENALTIES 4 AND 5 (z) + z = (Penalization.Crossing * checker * (1 - non_crossing_status)) + (Penalization.Feeders * excess_feeders * (1 - feeders_status)) + (sum(T[:, 4])) + # %% Forming the array of constraints + cons = np.array([connected_status, edges_count_status, capacity_status, non_crossing_status, feeders_status]) +# return {'Tree':T, #Variable T +# 'Cost':z, #Variable z +# 'Cons':cons, } #Variable cons +# print(T.shape) + return T, z, cons + + +if __name__ == '__main__': + pop = np.array([1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0]).astype(bool) + # pop=np.array([0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,1,0,1,0,0,0,0,0]).astype(bool) + print(cost_function(pop, W, WindFarm, Penalization, cables)) +# pop = np.array([0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,1,1,0,0,1,1,1,1,1,1,1,1,0,1,0,0,0,0,0,1,1,0,1,1,1,0,1,0]) +# W = np.array([1,2,2943,40792056433, +# 1,3,2696.56881408162, +# 1,4,2502.57234543653, +# 1,5,1722.54666356437, +# 1,6,1903.98993489193, +# 1,7,2870.00005856844, +# 1,8,2627.69313617361, +# 1,9,1305.96147097962, +# 1,10,678.241483155260, +# 1,11,1779.94372249856, +# 1,12,1867.24086171472, +# 1,13,580.973375657484, +# 1,14,1068.72994287077, +# 1,15,2429.10463306015, +# 1,16,2882.76364346409, +# 1,17,1804.91920412533, +# 1,18,1469.30338690226, +# 1,19,2239.12562222500, +# 1,20,2493.63307468915, +# 1,21,2677.59266922772, +# 2,4,1233.42707859998, +# 2,5,1262.80000000000, +# 3,5,1233.42707859998, +# 3,6,1262.80000000000, +# 4,8,1233.42707859998, +# 5,9,1233.42707859998, +# 6,7,1406.00711235754, +# 6,10,1233.42707859998, +# 6,11,1262.80000000000, +# 7,11,1233.42707859998, +# 8,12,1262.80000000000, +# 9,12,1233.42707859998, +# 10,13,1233.42707859998, +# 11,14,1233.42707859998, +# 11,15,1262.80000000000, +# 12,16,1233.42707859998, +# 13,17,1233.42707859998, +# 14,18,1233.42707859998, +# 15,19,1233.42707859998, +# 16,17,1406.00711235754, +# 17,20,1262.80000000000, +# 18,20,1233.42707859998, +# 18,21,1262.80000000000, +# 19,21,1233.42707859998]) +# from data import get_edges, get_n_wt +# %% EXTERNAL INPUTS FOR THE +# Edges = get_edges() +# class WindFarmObject(): +# def __init__(self, P=3.6): +# self.P = P +# self.GV = 33 +# self.F = 50 +# self.Feeders = 4 +# WindFarm = WindFarmObject() +# WindFarm.VarSize = Edges.shape[0] # Complete number of edges (variables) +# class PenalizationObject(): +# def __init__(self): +# pass +# n_wt = int(get_n_wt()) +# +# class cablesObject(): +# def __init__(self): +# self.ID = np.array([1,2,3,4,5,6,7,8,9,10,11]) # cables ID +# self.CrossSection = np.array([95,120,150,185,240,300,400,500,630,800,1000]) # cables cross section [mm2] +# self.NomCurrent = np.array([300,340,375,420,480,530,590,655,715,775,825]) # Current capacity [A] +# self.Sn = np.array([17.15,19.43,21.43,24.01,27.44,30.29,33.72,37.44,40.87,44.3,47.16]) +# self.Price = np.array([223742,240134,255792,277908,311267,342883,386052,440203,498064,564661,627778]) #Unitary price [euros/km] +# class SettingsObject(): +# def __init__(self): +# self.MaxIt = 200 #Maximum number of iterations +# self.StallIt = 1000 #Maximum number of iterations without change of the fitness value +# self.nPop = 100 #Number of individuals per generation +# self.pc = 0.2 #Crossover percentage +# self.pm = 0.2 #Mutation percentage 1 pair (Value not used, it is hardcoded in each iteration) NR +# self.pm2 = 0.1 #Mutation percentage 2 pairs of variables (Value not used, it is hardcoded in each iteration) NR +# self.pm3 = 0.1 #Mutation percentage 3 pairs of variables (Value not used, it is hardcoded in each iteration) NR +# self.pm4 = 0.1 #Mutation percentage 1 variable (Value not used, it is hardcoded in each iteration) NR +# self.AnimatedPlot = 1 #Animated plot status [0=off, 1=on] +# self.PEdgesCut = 0.1 #Search space, reduces percentage of edges explored in the optimization by removing the larger ones for each node. All edges to the substation are always considered [1-0] +# self.PerformancePlots = 1 #Perfomance plots status: Creates plots related to the time performance of the GA [0=off, 1=on] +# self.cablesAvailable = np.array([7, 9, 11])-1 #cabless used for optimization process. Examples: [1:11], [1,3,6], [1:3]. +# self.beta=8 +# %% Arranging classes +# Settings=SettingsObject() +# +# cables=cablesObject() +# cables.Available = Settings.cablesAvailable #cabless considered for optimization (structure) +# cables.Sbase = WindFarm.P #Apparent power of WTs (MVA) +# cables.Vbase = WindFarm.GV #Grid voltage (kV) (22, 33, 45, 66, 132, 220) +# cables.Freq = WindFarm.F #Frequency (Hz) +# +# cables.ID =cables.ID[cables.Available] # +# cables.CrossSection = cables.CrossSection[cables.Available] #cables cross section (mm2)(Only cables considered for opt) +# cables.NomCurrent= cables.NomCurrent[cables.Available] +# cables.Sn= cables.Sn[cables.Available] #cables apparent power capacity [Only cables considered for opt) +# cables.Capacity = np.floor(cables.Sn/cables.Sbase) #Maximum amount of WT supported for each cable +# cables.MaxCap = np.max(cables.Capacity) #Maximum amount of WT supported from all cables +# Penalization = PenalizationObject() +# Penalization.BaseRough = (np.max(Edges[:,2])*(n_wt-1))*np.max(cables.Price) # Find base penalization according to the number of edges and the total length of them. +# Penalization.Base = np.floor(np.log10(Penalization.BaseRough)) # Find order of magnitude of base penalization. +# Penalization.ConnectedComponents = 10**(Penalization.Base+5) # Base penalization: Total connecitvity constraint +# Penalization.EdgesCount = 10**(Penalization.Base+4) # Base penalization: Edges = Nodes - 1 constraint +# Penalization.NodesFeeder = 10**(Penalization.Base+2) # Base penalization: cables capacity constraint +# Penalization.Crossing = 10**(Penalization.Base+1) # Base penalization: cables crossings constraint +# Penalization.Feeders = 10**(Penalization.Base+1) # Base penalization: Number of feeders connected to OSS +# +# cost_function(pop,W,WindFarm,Penalization,cables) +# diff --git a/ed_win/drivers/ga/Crossover.py b/ed_win/drivers/ga/Crossover.py new file mode 100644 index 0000000..c1511e6 --- /dev/null +++ b/ed_win/drivers/ga/Crossover.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Feb 12 13:58:11 2020 + +@author: juru +""" +import numpy as np +import scipy.io + + +def crossover(x1, x2): + """ + This is the uniform cross-over + """ + """ + if test_mode: + mat = scipy.io.loadmat('../alpha.mat') + alpha = mat['alpha_all'][k] + else: + if not seed==None: + np.random.seed(seed) + """ + alpha = np.random.randint(0, 2, x1.size).ravel().astype(bool) + y1 = alpha * x1 + (1 - alpha) * x2 + y2 = alpha * x2 + (1 - alpha) * x1 + y1 = y1.astype(bool) + y2 = y2.astype(bool) + return y1, y2 + + +if __name__ == '__main__': + x1 = np.array([False, False, True, False, False, False, False, True, False, + True, False, False, True, True, False, True, True, True, + True, True, True, False, False, False, True, False, True, + False, False, False, False, False, True, False, False, False, + False, False, True, True, False, False, False, True, True, + False, False, True, False, False, False, True, True, False, + True, False, True, False, True, False, True, False, False, + False, False, False, False, True, False, False, True, False, + True, False, False, True, False, True, False, False, False, + False, True, False, False, True, True]) + x2 = np.array([True, True, False, False, True, True, False, True, True, + True, False, True, False, False, False, False, True, False, + True, False, False, True, False, True, False, False, True, + True, True, False, False, False, True, True, False, True, + False, True, False, False, True, False, False, False, False, + False, True, True, True, True, False, True, False, True, + True, True, True, False, False, True, True, True, True, + True, True, False, False, True, True, False, True, False, + True, False, False, True, True, True, True, True, True, + False, True, False, True, True, True]) + + print(crossover(x1, x2)) diff --git a/ed_win/drivers/ga/FW_2_Solver_GA.py b/ed_win/drivers/ga/FW_2_Solver_GA.py new file mode 100644 index 0000000..1dca1b6 --- /dev/null +++ b/ed_win/drivers/ga/FW_2_Solver_GA.py @@ -0,0 +1,500 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Jan 9 09:19:24 2020 + +@author: mikf +""" + +import copy +import scipy.io +import matplotlib.pyplot as plt +import numpy as np +from numpy import newaxis as na + +from ed_win.drivers.ga.data import Mat2Py +from ed_win.drivers.ga.CostFunction import cost_function +from ed_win.drivers.ga.RouletteWheelSelection import roulette_wheel_selection +from ed_win.drivers.ga.Crossover import crossover +from ed_win.drivers.ga.Mutate import mutate +from dict_to_class import DictToClass + + +class WindFarm(DictToClass): + def __init__(self, **kwargs): + self._get_default() + super().__init__(kwargs) + + def _get_default(self): + self.P = 3.6 + self.GV = 33 + self.F = 50 + self.Feeders = 4 + + +class Cable(DictToClass): + def __init__(self, **kwargs): + self._get_default() + super().__init__(kwargs) + + def _get_default(self): + self.CrossSection = np.array([100, 500, 1000]) + self.Capacities = np.array([2, 4, 6]) + self.Price = np.array([223742, 340134, 555792]) # Unitary price [euros/km] + + +class Settings(DictToClass): + def __init__(self, **kwargs): + self._get_default() + super().__init__(kwargs) + + def _get_default(self): + self.MaxIt = 150 # Maximum number of iterations + self.StallIt = 10 # Maximum number of iterations without change of the fitness value + self.nPop = 100 # Number of individuals per generation + self.pc = 0.2 # Crossover percentage + self.pm = 0.2 # Mutation percentage 1 pair (Value not used, it is hardcoded in each iteration) NR + self.pm2 = 0.1 # Mutation percentage 2 pairs of variables (Value not used, it is hardcoded in each iteration) NR + self.pm3 = 0.1 # Mutation percentage 3 pairs of variables (Value not used, it is hardcoded in each iteration) NR + self.pm4 = 0.1 # Mutation percentage 1 variable (Value not used, it is hardcoded in each iteration) NR + self.AnimatedPlot = 1 # Animated plot status [0=off, 1=on] + self.PEdgesCut = 0.3 # Search space, reduces percentage of edges explored in the optimization by removing the larger ones for each node. All edges to the substation are always considered [1-0] + self.PerformancePlots = 1 # Perfomance plots status: Creates plots related to the time performance of the GA [0=off, 1=on] + # self.CableAvailable = np.array([7, 9, 11])-1 #Cables used for optimization process. Examples: [1:11], [1,3,6], [1:3]. + self.beta = 8 + + +class Penalization(DictToClass): + def __init__(self, Cable, Edges, n_wt, **kwargs): + self.Cable = Cable + self.Edges = Edges + self.n_wt = n_wt + self._get_default() + super().__init__(kwargs) + + def _get_default(self): + self.BaseRough = (np.max(self.Edges[:, 2]) * (self.n_wt - 1)) * np.max( + self.Cable.Price) # Find base penalization according to the number of edges and the total length of them. + self.Base = np.floor(np.log10(self.BaseRough)) # Find order of magnitude of base penalization. + self.ConnectedComponents = 10 ** (self.Base + 5) # Base penalization: Total connecitvity constraint + self.EdgesCount = 10 ** (self.Base + 4) # Base penalization: Edges = Nodes - 1 constraint + self.NodesFeeder = 10 ** (self.Base + 2) # Base penalization: Cable capacity constraint + self.Crossing = 10 ** (self.Base + 1) # Base penalization: Cable crossings constraint + self.Feeders = 10 ** (self.Base + 1) # Base penalization: Number of feeders connected to OSS + + +def upper_tri_masking(A): + m = A.shape[0] + r = np.arange(m) + mask = r[:, None] < r + return A[mask] + + +def xy_to_edges(x, y, x0, y0): + CoordX = np.append(x0, x) + CoordY = np.append(y0, y) + n_wt = int(CoordX.size) + dx = CoordX[:, na] - CoordX[na, :] + dy = CoordY[:, na] - CoordY[na, :] + lengths = np.sqrt(dx ** 2 + dy ** 2) + lengths = upper_tri_masking(lengths).ravel() + nodes = np.zeros((n_wt, n_wt, 2)) + nodes[:, :, 0] = np.repeat(np.arange(1, n_wt + 1), n_wt).reshape(n_wt, n_wt) + nodes[:, :, 1] = np.tile(np.arange(1, n_wt + 1), n_wt).reshape(n_wt, n_wt) + nodes = upper_tri_masking(nodes) + edges = np.hstack((nodes, lengths[:, na])) + return CoordX, CoordY, n_wt, edges + + +class ECSGA(): # Electrical Collection System Genetic Algorithm + def __init__(self, WindFarm, Cable, Settings, verbose=False, CoordX=None, CoordY=None): + self.verbose = verbose + # %% EXTERNAL INPUTS FOR THE GA + # Cable.Available = Settings.CableAvailable #Cables considered for optimization (structure) + + # Cable.ID =Cable.ID[Cable.Available] # + # Cable.CrossSection = Cable.CrossSection[Cable.Available] #Cable cross section (mm2)(Only cables considered for opt) + # Cable.NomCurrent= Cable.NomCurrent[Cable.Available] + # Cable.Sn= Cable.Sn[Cable.Available] #Cable apparent power capacity [Only cables considered for opt) + # Cable.Capacities = np.floor(Cable.Sn/Cable.Sbase) #Maximum amount of WT supported for each cable + # Cable.Price=Cable.Price[Cable.Available] + + self.WindFarm = WindFarm + self.Cable = Cable + self.Settings = Settings + Cable.MaxCap = np.max(Cable.Capacities) # Maximum amount of WT supported from all cables + + self.state = None + self.CoordX = CoordX + self.CoordY = CoordY + + # + def run(self, x=None, y=None, x0=None, y0=None, state=None): + if not isinstance(x, type(None)): + CoordX, CoordY, n_wt, Edges = xy_to_edges(x, y, x0, y0) + self.CoordX = CoordX + self.CoordY = CoordY + penalization = Penalization(self.Cable, Edges, n_wt) + + # %% Filtering search space according the input PEdgesCut + self.WindFarm.VarSize = Edges.shape[0] # Complete number of edges (variables) + PEdgesCut = self.Settings.PEdgesCut # Per unit of smaller edges for each node to be kept + + EdgesCut = int(np.round(PEdgesCut * (n_wt - 1), 0)) # Number of smaller edges for each node to be kept + Edges3 = None # New list of edges + + # Reduce the number of edges to the predetermined percentage, all nodes + # keep the edges to OSS no matter the distance. + for i in range(int(n_wt)): + Edges1 = Edges[Edges[:, 0] == i + 1] + Edges2 = Edges[Edges[:, 1] == i + 1] + Edges12 = np.append(Edges1, Edges2, axis=0) + Edges12 = Edges12[Edges12[:, 2].argsort()] + if i > 0: + Edges12 = Edges12[:EdgesCut, :] + if isinstance(Edges3, type(None)): + Edges3 = Edges12 + else: + Edges3 = np.append(Edges3, Edges12, axis=0) + Edges3 = np.unique(Edges3, axis=0) + if state: + state_pos = np.full((Edges3.shape[0]), False) + for s in range(state['edges'].shape[0]): + remap = (state['edges'][s, 0].astype(int) == Edges3[:, 0].astype(int)) & ( + state['edges'][s, 1].astype(int) == Edges3[:, 1].astype(int)) + if remap.sum() == 1: + state_pos[remap] = True + elif remap.sum() == 0: + remap2 = (state['edges'][s, 0].astype(int) == Edges[:, 0].astype(int)) & ( + state['edges'][s, 1].astype(int) == Edges[:, 1].astype(int)) + Edges3 = np.append(Edges3, Edges[remap2], axis=0) + state_pos = np.append(state_pos, True) + else: + 'this should not happen' + # + # % Newly reduced set of edges (variables) + Edges = Edges3 + W = Edges3 + # + self.WindFarm.NewVarSize = Edges.shape[0] # Search space considered: New number of variables after applying PEdgesCut + # + nVar = Edges.shape[0] # Number of Decision Variables + VarSize = (1, nVar) # Decision Variables Matrix Size + # + + # %% Stopping criteria for GA and pre-setting mutation parameters + MaxIt = self.Settings.MaxIt # Maximum Number of Iterations to stop + StallIt = self.Settings.StallIt # Number of iterations w/o change to stop + + # Population size + nPop = self.Settings.nPop # Population Size + + # Crossover parameters + pc = self.Settings.pc # Crossover Percentage + nc = 2 * np.round(pc * nPop / 2, 0) # Number of Offsprings (also Parents) + + # Mutation parameters + # Mutation 1 pair of edges at the same time + pm = self.Settings.pm # Mutation Percentage 1 + nm = np.round(pm * nPop, 0).astype(int) # Number of Mutants 1 + mu = 2 * 1 / nVar # Mutation Rate 1 + # Mutation 2 pairs of edges at the same time + pm2 = self.Settings.pm2 # Mutation Percentage 2 + nm2 = np.round(pm2 * nPop, 0) # Number of Mutants 2 + mu2 = 4 * 1 / nVar # Mutation Rate 2 + # Mutation 3 pairs of edges at the same time + pm3 = self.Settings.pm3 # Mutation Percentage 3 + nm3 = np.round(pm3 * nPop, 0) # Number of Mutants 3 + mu3 = 6 * 1 / nVar # Mutation Rate 3 + # Mutation 1 edge + pm4 = self.Settings.pm4 # Mutatuon Percentage 4 + nm4 = np.round(pm4 * nPop, 0) # Number of Mutants 4 + mu4 = 1 / nVar # Mutation Rate 4 + # %% GA Penalization factors + # %% + # TODO: CAN WE INDEX X ? + # + # TODO: vectorize this loop. Create the numpy array directly and not through Pandas? + # + position_ij = np.full((nPop, nVar), False) # pop + cons_ik = np.full((nPop, 5), False) + cost_i = np.zeros((nPop)) + tree_i = np.full((nPop), None) + for i in range(nPop): + if state and i == 0: # if initialized with state, include this in the new populations. + pop = state_pos + else: + pop = np.random.randint(0, 2, VarSize).ravel().astype(bool) + position_ij[i, :] = pop + tree, cost, cons = cost_function(pop, W, self.WindFarm, penalization, self.Cable, n_wt, CoordX, CoordY) + cons_ik[i, :] = cons + cost_i[i] = cost + tree_i[i] = tree + + sort_index = cost_i.argsort() + position_ij, cons_ik, cost_i, tree_i = position_ij[sort_index], cons_ik[sort_index], cost_i[sort_index], tree_i[sort_index] + beta = self.Settings.beta + worst_cost = cost_i[-1] + termination_cost = [] + + for it in range(MaxIt): + # GA parameters can be changed at each stage (constraints met) of + # the iterative process. Mainly the parameters changed relate to + # the mutation operator + if (cons_ik[0] == np.array([1, 0, 0, 0, 0])).all(): + pm = 0.4 # Per unit of the population with 1 pair of variable mutation + nm = np.ceil(pm * nPop).astype(int) # Number of individuals with a 1 pair of variable mutation (Has to be rounded) + pm2 = 0.01 # 2 pairs of variables mutation + nm2 = np.ceil(pm2 * nPop).astype(int) + pm3 = 0.8 # 3 pairs of variables mutation + nm3 = np.ceil(pm3 * nPop).astype(int) + pm4 = 0.001 # 1 variable mutation + nm4 = np.ceil(pm4 * nPop).astype(int) + elif (cons_ik[0] == [1, 1, 0, 0, 0]).all(): + pm = 0.8 + nm = np.ceil(pm * nPop).astype(int) + pm2 = 0.2 + nm2 = np.ceil(pm2 * nPop).astype(int) + pm3 = 0.4 + nm3 = np.ceil(pm3 * nPop).astype(int) + pm4 = 0.001 + nm4 = np.ceil(pm4 * nPop).astype(int) + elif (cons_ik[0] == [1, 1, 1, 0, 0]).all(): + pm = 1.2 + nm = np.ceil(pm * nPop).astype(int) + pm2 = 0.3 + nm2 = np.ceil(pm2 * nPop).astype(int) + pm3 = 0.4 + nm3 = np.ceil(pm3 * nPop).astype(int) + pm4 = 0.0001 + nm4 = np.ceil(pm4 * nPop).astype(int) + elif (cons_ik[0] == [1, 1, 1, 0, 1]).all(): + pm = 3 + nm = np.ceil(pm * nPop).astype(int) + pm2 = 1 + nm2 = np.ceil(pm2 * nPop).astype(int) + pm3 = 1 + nm3 = np.ceil(pm3 * nPop).astype(int) + pm4 = 0.2 + nm4 = np.ceil(pm4 * nPop).astype(int) + elif (cons_ik[0] == [1, 1, 1, 1, 1]).all(): + pm = 3 + nm = np.ceil(pm * nPop).astype(int) + pm2 = 1 + nm2 = np.ceil(pm2 * nPop).astype(int) + pm3 = 1 + nm3 = np.ceil(pm3 * nPop).astype(int) + pm4 = 0.2 + nm4 = np.ceil(pm4 * nPop).astype(int) + + P = np.exp(-beta * cost_i / worst_cost) + P = P / np.sum(P) + position_cj = np.full((int(nc / 2) * 2, nVar), False) # pop + cons_ck = np.full((int(nc / 2) * 2, 5), False) + cost_c = np.zeros((int(nc / 2) * 2)) + tree_c = np.full((int(nc / 2) * 2), None) + + for k in range(int(nc / 2)): + # Select Parents Indices + i1 = roulette_wheel_selection(P) + i2 = roulette_wheel_selection(P) + + # Select Parents + p1 = copy.deepcopy(position_ij[i1]) + p2 = copy.deepcopy(position_ij[i2]) + + # Perform Crossover + y1, y2 = crossover(p1, p2) + + tree, cost, cons = cost_function(y1, W, self.WindFarm, penalization, self.Cable, n_wt, CoordX, CoordY) + position_cj[k, :] = y1 + cons_ck[k, :] = cons + cost_c[k] = cost + tree_c[k] = tree + + tree, cost, cons = cost_function(y2, W, self.WindFarm, penalization, self.Cable, n_wt, CoordX, CoordY) + position_cj[int(k + nc / 2), :] = y2 + cons_ck[int(k + nc / 2), :] = cons + cost_c[int(k + nc / 2)] = cost + tree_c[int(k + nc / 2)] = tree + # %% + """ + m : number of mutations, nm (changes with outer loop) + f : mutation type (4 types of mutations) + j : number of variables, nVar + k : number of constraints (5 types of constraints) + """ + position_m1j = np.full((nm, nVar), False) # pop + cons_m1k = np.full((nm, 5), False) + cost_m1 = np.zeros(nm) + tree_m1 = np.full(nm, None) + + position_m2j = np.full((nm2, nVar), False) # pop + cons_m2k = np.full((nm2, 5), False) + cost_m2 = np.zeros(nm2) + tree_m2 = np.full(nm2, None) + + position_m3j = np.full((nm3, nVar), False) # pop + cons_m3k = np.full((nm3, 5), False) + cost_m3 = np.zeros(nm3) + tree_m3 = np.full(nm3, None) + + position_m4j = np.full((nm4, nVar), False) # pop + cons_m4k = np.full((nm4, 5), False) + cost_m4 = np.zeros(nm4) + tree_m4 = np.full(nm4, None) + + for k in range(nm): + index = np.random.randint(0, nPop) + + # Perform Mutation + pom1 = copy.deepcopy(position_ij[index]) + xm1 = mutate(pom1, mu) + + tree, cost, cons = cost_function(xm1, W, self.WindFarm, penalization, self.Cable, n_wt, CoordX, CoordY) + position_m1j[k, :] = xm1 + cons_m1k[k, :] = cons + cost_m1[k] = cost + tree_m1[k] = tree + + for k in range(nm2): + index = np.random.randint(0, nPop) + + # Perform Mutation + pom2 = copy.deepcopy(position_ij[index]) + xm2 = mutate(pom2, mu2) + + tree, cost, cons = cost_function(xm2, W, self.WindFarm, penalization, self.Cable, n_wt, CoordX, CoordY) + position_m2j[k, :] = xm2 + cons_m2k[k, :] = cons + cost_m2[k] = cost + tree_m2[k] = tree + + for k in range(nm3): + index = np.random.randint(0, nPop) + + # Perform Mutation + pom3 = copy.deepcopy(position_ij[index]) + xm3 = mutate(pom3, mu3) + + tree, cost, cons = cost_function(xm3, W, self.WindFarm, penalization, self.Cable, n_wt, CoordX, CoordY) + position_m3j[k, :] = xm3 + cons_m3k[k, :] = cons + cost_m3[k] = cost + tree_m3[k] = tree + for k in range(nm4): + index = np.random.randint(0, nPop) + + # Perform Mutation + pom4 = copy.deepcopy(position_ij[index]) + xm4 = mutate(pom4, mu4) + + tree, cost, cons = cost_function(xm4, W, self.WindFarm, penalization, self.Cable, n_wt, CoordX, CoordY) + position_m4j[k, :] = xm4 + cons_m4k[k, :] = cons + cost_m4[k] = cost + tree_m4[k] = tree + + position_ij = np.vstack([position_ij, position_cj, position_m1j, position_m2j, position_m3j, position_m4j]) + cons_ik = np.vstack([cons_ik, cons_ck, cons_m1k, cons_m2k, cons_m3k, cons_m4k]) + cost_i = np.vstack([cost_i[:, na], cost_c[:, na], cost_m1[:, na], cost_m2[:, na], cost_m3[:, na], cost_m4[:, na]]).ravel() + tree_i = np.vstack([tree_i[:, na], tree_c[:, na], tree_m1[:, na], tree_m2[:, na], tree_m3[:, na], tree_m4[:, na]]).ravel() + + sort_index = cost_i.argsort() + position_ij, cons_ik, cost_i, tree_i = position_ij[sort_index, :], cons_ik[sort_index, :], cost_i[sort_index], tree_i[sort_index] + + worst_cost = max(worst_cost, cost_i[-1]) + position_ij, cons_ik, cost_i, tree_i = position_ij[:nPop, :], cons_ik[:nPop, :], cost_i[:nPop], tree_i[:nPop] + termination_cost.append(cost_i[0]) + if self.verbose: + print(it) + print(cons_ik[0]) + print(cost_i[0]) + self.tree_i = tree_i + state = {'pos': position_ij[0, :], + 'cons': cons_ik[0, :], + 'cost': cost_i[0], + 'tree': tree_i[0], + 'edges': W[position_ij[0, :]][:, 0:2]} + if len(termination_cost) > StallIt and (len(set(termination_cost[-(StallIt + 1):])) == 1 and (cons_ik[0]).all()): + break + self.state = state + return cost_i[0], state + + def plot(self): + CoordX = self.CoordX + CoordY = self.CoordY + tree_i = self.tree_i + # plt.close() + plt.plot(CoordX[1:], CoordY[1:], 'r+', markersize=10, label='Turbines') + plt.plot(CoordX[0], CoordY[0], 'ro', markersize=10, label='OSS') + colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'bg', 'gr', 'rc', 'cm'] + + if (tree_i[0].shape)[1] == 2: # If no feasible solution was found + n1xs = CoordX[(tree_i[0].T[0:1] - 1).astype(int)].ravel().T + n2xs = CoordX[(tree_i[0].T[1:2] - 1).astype(int)].ravel().T + n1ys = CoordY[(tree_i[0].T[0:1] - 1).astype(int)].ravel().T + n2ys = CoordY[(tree_i[0].T[1:2] - 1).astype(int)].ravel().T + xs = np.vstack([n1xs, n2xs]) + ys = np.vstack([n1ys, n2ys]) + plt.plot(xs, ys, '{}'.format(colors[0])) + plt.plot([], [], '{}'.format(colors[0]), label='N/A') + else: + for n, cable_type in enumerate(self.Cable.CrossSection): + index = tree_i[0][:, 3] == n + if index.any(): + n1xs = CoordX[(tree_i[0][index].T[0:1] - 1).astype(int)].ravel().T + n2xs = CoordX[(tree_i[0][index].T[1:2] - 1).astype(int)].ravel().T + n1ys = CoordY[(tree_i[0][index].T[0:1] - 1).astype(int)].ravel().T + n2ys = CoordY[(tree_i[0][index].T[1:2] - 1).astype(int)].ravel().T + xs = np.vstack([n1xs, n2xs]) + ys = np.vstack([n1ys, n2ys]) + plt.plot(xs, ys, '{}'.format(colors[n])) + plt.plot([], [], '{}'.format(colors[n]), label='Cable: {} mm2'.format(self.Cable.CrossSection[n])) + # plt.plot(xs,ys,color=HSV_tuples[n]) + # plt.plot([],[],color=HSV_tuples[n],label='Cable: {} mm2'.format(Cable.CrossSection[n])) + plt.legend() + + +if __name__ == '__main__': + x = np.array([492039.7611, 490820.2714, 489321.8153, 488102.3256, + 486603.8695, 497753.2809, 496533.7913, 495035.3352, + 493815.8455, 492317.3894, 489599.4436, 498030.9093, + 496811.4196, 495312.9635]) + y = np.array([5731802.533, 5732985.596, 5732184.707, 5733367.77, + 5732566.881, 5733012.62, 5734195.683, 5733394.794, + 5734577.858, 5733776.968, 5734159.143, 5734987.056, + 5736170.119, 5735369.23]) + + x_oss = 497620.7 + y_oss = 5730622.0 + + # x = np.array([7113.10301763, 7924.86160243, 7564.14443282, + # 8375.90301763, 9187.66160243, 9999.42018723, 8015.18584802, + # 8826.94443282, 9638.70301763, 10450.46160243, 9277.98584802, + # 10089.74443282, 10901.50301763, 11713.26160243, 9729.02726322, + # 10540.78584802, 11352.54443282, 12164.30301763, 11803.58584802, + # 12615.34443282]) + # y = np.array([9426, 8278, 10574, 9426, 8278, 7130, 11722, 10574, + # 9426, 8278, 11722, 10574, 9426, 8278, 12870, 11722, 10574, + # 9426, 11722, 10574]) + + x0, y0 = (x_oss, y_oss) + + ecsga = ECSGA(WindFarm(), + Cable(), + Settings(MaxIt=5000, StallIt=100), + verbose=True) + plt.close('all') + cable_cost, state = ecsga.run(x, y, x0, y0, ecsga.state) + ecsga.plot() + plt.show() + """ + for i in range(5): + cable_cost, state = ecsga.run(x, y, x0, y0, ecsga.state) + x[np.random.randint(0, len(x) - 1)] *= (100 + np.random.randint(-5, 5)) / 100 + y[np.random.randint(0, len(y) - 1)] *= (100 + np.random.randint(-5, 5)) / 100 + print(i, cable_cost) + plt.figure(i) + ecsga.plot() + plt.show() + """ diff --git a/ed_win/drivers/ga/GA.py b/ed_win/drivers/ga/GA.py new file mode 100644 index 0000000..7d238da --- /dev/null +++ b/ed_win/drivers/ga/GA.py @@ -0,0 +1,532 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Jan 9 09:19:24 2020 + +@author: mikf +""" + +import copy +import scipy.io +import matplotlib.pyplot as plt +import numpy as np +from numpy import newaxis as na +import networkx as nx + +# from .data import Mat2Py +# from .CostFunction import cost_function +# from .RouletteWheelSelection import roulette_wheel_selection +# from .Crossover import crossover +# from .Mutate import mutate +# from .dict_to_class import DictToClass + +from ed_win.drivers.ga.data import Mat2Py +from ed_win.drivers.ga.CostFunction import cost_function +from ed_win.drivers.ga.RouletteWheelSelection import roulette_wheel_selection +from ed_win.drivers.ga.Crossover import crossover +from ed_win.drivers.ga.Mutate import mutate +from .dict_to_class import DictToClass + + +class WindFarm(DictToClass): + def __init__(self, **kwargs): + self._get_default() + super().__init__(kwargs) + + def _get_default(self): + self.P = 3.6 + self.GV = 33 + self.F = 50 + self.Feeders = 4 + + +class Cables(DictToClass): + def __init__(self, **kwargs): + self._get_default() + super().__init__(kwargs) + + def _get_default(self): + self.CrossSection = np.array([]) + self.Capacity = np.array([]) + self.Price = np.array([]) # Unitary price [euros/km] + + +class Settings(DictToClass): + def __init__(self, **kwargs): + self._get_default() + super().__init__(kwargs) + + def _get_default(self): + self.MaxIt = 150 # Maximum number of iterations + self.StallIt = 10 # Maximum number of iterations without change of the fitness value + self.nPop = 100 # Number of individuals per generation + self.pc = 0.2 # Crossover percentage + self.pm = 0.2 # Mutation percentage 1 pair (Value not used, it is hardcoded in each iteration) NR + self.pm2 = 0.1 # Mutation percentage 2 pairs of variables (Value not used, it is hardcoded in each iteration) NR + self.pm3 = 0.1 # Mutation percentage 3 pairs of variables (Value not used, it is hardcoded in each iteration) NR + self.pm4 = 0.1 # Mutation percentage 1 variable (Value not used, it is hardcoded in each iteration) NR + self.AnimatedPlot = 1 # Animated plot status [0=off, 1=on] + self.PEdgesCut = 0.3 # Search space, reduces percentage of edges explored in the optimization by removing the larger ones for each node. All edges to the substation are always considered [1-0] + self.PerformancePlots = 1 # Perfomance plots status: Creates plots related to the time performance of the GA [0=off, 1=on] + # self.cablesAvailable = np.array([7, 9, 11])-1 #cabless used for optimization process. Examples: [1:11], [1,3,6], [1:3]. + self.beta = 8 + + +class Penalization(DictToClass): + def __init__(self, cables, Edges, n_wt, **kwargs): + self.cables = cables + self.Edges = Edges + self.n_wt = n_wt + self._get_default() + super().__init__(kwargs) + + def _get_default(self): + self.BaseRough = (np.max(self.Edges[:, 2]) * (self.n_wt - 1)) * np.max(self.cables.Price) # Find base penalization according to the number of edges and the total length of them. + self.Base = np.floor(np.log10(self.BaseRough)) # Find order of magnitude of base penalization. + self.ConnectedComponents = 10**(self.Base + 5) # Base penalization: Total connecitvity constraint + self.EdgesCount = 10**(self.Base + 4) # Base penalization: Edges = Nodes - 1 constraint + self.NodesFeeder = 10**(self.Base + 2) # Base penalization: cables capacity constraint + self.Crossing = 10**(self.Base + 1) # Base penalization: cables crossings constraint + self.Feeders = 10**(self.Base + 1) # Base penalization: Number of feeders connected to OSS + + +def upper_tri_masking(A): + m = A.shape[0] + r = np.arange(m) + mask = r[:, None] < r + return A[mask] + + +def xy_to_edges(x, y, x0, y0): + CoordX = np.append(x0, x) + CoordY = np.append(y0, y) + n_wt = int(CoordX.size) + dx = CoordX[:, na] - CoordX[na, :] + dy = CoordY[:, na] - CoordY[na, :] + lengths = np.sqrt(dx ** 2 + dy ** 2) + lengths = upper_tri_masking(lengths).ravel() + nodes = np.zeros((n_wt, n_wt, 2)) + nodes[:, :, 0] = np.repeat(np.arange(1, n_wt + 1), n_wt).reshape(n_wt, n_wt) + nodes[:, :, 1] = np.tile(np.arange(1, n_wt + 1), n_wt).reshape(n_wt, n_wt) + nodes = upper_tri_masking(nodes) + edges = np.hstack((nodes, lengths[:, na])) + return CoordX, CoordY, n_wt, edges + + +class GA(): # Electrical Collection System Genetic Algorithm + def __init__(self, G, wf=None, settings={}, verbose=False): + self.verbose = True + # %% EXTERNAL INPUTS FOR THE GA + # cables.Available = Settings.cablesAvailable #cabless considered for optimization (structure) + + # cables.ID =cables.ID[cables.Available] # + # cables.CrossSection = cables.CrossSection[cables.Available] #cables cross section (mm2)(Only cables considered for opt) + # cables.NomCurrent= cables.NomCurrent[cables.Available] + # cables.Sn= cables.Sn[cables.Available] #cables apparent power capacity [Only cables considered for opt) + # cables.Capacities = np.floor(cables.Sn/cables.Sbase) #Maximum amount of WT supported for each cable + # cables.Price=cables.Price[cables.Available] + self.G = G + c = Cables() + cables = G.graph['cables'] + c.CrossSection = cables['area'] + c.Capacity = cables['capacity'] + c.Price = cables['cost'] + self.cables = c + self.cables.MaxCap = np.max(self.cables.Capacity) # Maximum amount of WT supported from all cables + self.WindFarm = wf if wf is not None else WindFarm() + self.Settings = Settings(**settings) + self.state = None + + # Run and get the result + self.H = self.run() + + def run(self): + state = self.state + # if not isinstance(x, type(None)): + # CoordX, CoordY, n_wt, Edges = xy_to_edges(x, y, x0, y0) + # self.CoordX = CoordX + # self.CoordY = CoordY + M = self.G.graph['M'] + x = self.G.graph['VertexC'][0:-M, 0] + y = self.G.graph['VertexC'][0:-M, 1] + x0, y0 = (self.G.graph['VertexC'][-M:, 0], self.G.graph['VertexC'][-M:, 1]) + CoordX, CoordY, n_wt, Edges = xy_to_edges(x, y, x0, y0) + + penalization = Penalization(self.cables, Edges, n_wt) + + # %% Filtering search space according the input PEdgesCut + self.WindFarm.VarSize = Edges.shape[0] # Complete number of edges (variables) + PEdgesCut = self.Settings.PEdgesCut # Per unit of smaller edges for each node to be kept + + EdgesCut = int(np.round(PEdgesCut * (n_wt - 1), 0)) # Number of smaller edges for each node to be kept + Edges3 = None # New list of edges + + # Reduce the number of edges to the predetermined percentage, all nodes + # keep the edges to OSS no matter the distance. + for i in range(int(n_wt)): + Edges1 = Edges[Edges[:, 0] == i + 1] + Edges2 = Edges[Edges[:, 1] == i + 1] + Edges12 = np.append(Edges1, Edges2, axis=0) + Edges12 = Edges12[Edges12[:, 2].argsort()] + if i > 0: + Edges12 = Edges12[:EdgesCut, :] + if isinstance(Edges3, type(None)): + Edges3 = Edges12 + else: + Edges3 = np.append(Edges3, Edges12, axis=0) + Edges3 = np.unique(Edges3, axis=0) + if state: + state_pos = np.full((Edges3.shape[0]), False) + for s in range(state['edges'].shape[0]): + remap = (state['edges'][s, 0].astype(int) == Edges3[:, 0].astype(int)) & (state['edges'][s, 1].astype(int) == Edges3[:, 1].astype(int)) + if remap.sum() == 1: + state_pos[remap] = True + elif remap.sum() == 0: + remap2 = (state['edges'][s, 0].astype(int) == Edges[:, 0].astype(int)) & (state['edges'][s, 1].astype(int) == Edges[:, 1].astype(int)) + Edges3 = np.append(Edges3, Edges[remap2], axis=0) + state_pos = np.append(state_pos, True) + else: + 'this should not happen' + # + # % Newly reduced set of edges (variables) + Edges = Edges3 + W = Edges3 + # + self.WindFarm.NewVarSize = Edges.shape[0] # Search space considered: New number of variables after applying PEdgesCut + # + nVar = Edges.shape[0] # Number of Decision Variables + VarSize = (1, nVar) # Decision Variables Matrix Size + # + + # %% Stopping criteria for GA and pre-setting mutation parameters + MaxIt = self.Settings.MaxIt # Maximum Number of Iterations to stop + StallIt = self.Settings.StallIt # Number of iterations w/o change to stop + + # Population size + nPop = self.Settings.nPop # Population Size + + # Crossover parameters + pc = self.Settings.pc # Crossover Percentage + nc = 2 * np.round(pc * nPop / 2, 0) # Number of Offsprings (also Parents) + + # Mutation parameters + # Mutation 1 pair of edges at the same time + pm = self.Settings.pm # Mutation Percentage 1 + nm = np.round(pm * nPop, 0).astype(int) # Number of Mutants 1 + mu = 2 * 1 / nVar # Mutation Rate 1 + # Mutation 2 pairs of edges at the same time + pm2 = self.Settings.pm2 # Mutation Percentage 2 + nm2 = np.round(pm2 * nPop, 0) # Number of Mutants 2 + mu2 = 4 * 1 / nVar # Mutation Rate 2 + # Mutation 3 pairs of edges at the same time + pm3 = self.Settings.pm3 # Mutation Percentage 3 + nm3 = np.round(pm3 * nPop, 0) # Number of Mutants 3 + mu3 = 6 * 1 / nVar # Mutation Rate 3 + # Mutation 1 edge + pm4 = self.Settings.pm4 # Mutatuon Percentage 4 + nm4 = np.round(pm4 * nPop, 0) # Number of Mutants 4 + mu4 = 1 / nVar # Mutation Rate 4 + # %% GA Penalization factors + # %% + # TODO: CAN WE INDEX X ? + # + # TODO: vectorize this loop. Create the numpy array directly and not through Pandas? + # + position_ij = np.full((nPop, nVar), False) # pop + cons_ik = np.full((nPop, 5), False) + cost_i = np.zeros((nPop)) + tree_i = np.full((nPop), None) + for i in range(nPop): + if state and i == 0: # if initialized with state, include this in the new populations. + pop = state_pos + else: + pop = np.random.randint(0, 2, VarSize).ravel().astype(bool) + position_ij[i, :] = pop + tree, cost, cons = cost_function(pop, W, self.WindFarm, penalization, self.cables, n_wt, CoordX, CoordY) + cons_ik[i, :] = cons + cost_i[i] = cost + tree_i[i] = tree + + sort_index = cost_i.argsort() + position_ij, cons_ik, cost_i, tree_i = position_ij[sort_index], cons_ik[sort_index], cost_i[sort_index], tree_i[sort_index] + + beta = self.Settings.beta + worst_cost = cost_i[-1] + termination_cost = [] + + for it in range(MaxIt): + # GA parameters can be changed at each stage (constraints met) of + # the iterative process. Mainly the parameters changed relate to + # the mutation operator + if (cons_ik[0] == np.array([1, 0, 0, 0, 0])).all(): + pm = 0.4 # Per unit of the population with 1 pair of variable mutation + nm = np.ceil(pm * nPop).astype(int) # Number of individuals with a 1 pair of variable mutation (Has to be rounded) + pm2 = 0.01 # 2 pairs of variables mutation + nm2 = np.ceil(pm2 * nPop).astype(int) + pm3 = 0.8 # 3 pairs of variables mutation + nm3 = np.ceil(pm3 * nPop).astype(int) + pm4 = 0.001 # 1 variable mutation + nm4 = np.ceil(pm4 * nPop).astype(int) + elif (cons_ik[0] == [1, 1, 0, 0, 0]).all(): + pm = 0.8 + nm = np.ceil(pm * nPop).astype(int) + pm2 = 0.2 + nm2 = np.ceil(pm2 * nPop).astype(int) + pm3 = 0.4 + nm3 = np.ceil(pm3 * nPop).astype(int) + pm4 = 0.001 + nm4 = np.ceil(pm4 * nPop).astype(int) + elif (cons_ik[0] == [1, 1, 1, 0, 0]).all(): + pm = 1.2 + nm = np.ceil(pm * nPop).astype(int) + pm2 = 0.3 + nm2 = np.ceil(pm2 * nPop).astype(int) + pm3 = 0.4 + nm3 = np.ceil(pm3 * nPop).astype(int) + pm4 = 0.0001 + nm4 = np.ceil(pm4 * nPop).astype(int) + elif (cons_ik[0] == [1, 1, 1, 0, 1]).all(): + pm = 3 + nm = np.ceil(pm * nPop).astype(int) + pm2 = 1 + nm2 = np.ceil(pm2 * nPop).astype(int) + pm3 = 1 + nm3 = np.ceil(pm3 * nPop).astype(int) + pm4 = 0.2 + nm4 = np.ceil(pm4 * nPop).astype(int) + elif (cons_ik[0] == [1, 1, 1, 1, 1]).all(): + pm = 3 + nm = np.ceil(pm * nPop).astype(int) + pm2 = 1 + nm2 = np.ceil(pm2 * nPop).astype(int) + pm3 = 1 + nm3 = np.ceil(pm3 * nPop).astype(int) + pm4 = 0.2 + nm4 = np.ceil(pm4 * nPop).astype(int) + + P = np.exp(-beta * cost_i / worst_cost) + P = P / np.sum(P) + position_cj = np.full((int(nc / 2) * 2, nVar), False) # pop + cons_ck = np.full((int(nc / 2) * 2, 5), False) + cost_c = np.zeros((int(nc / 2) * 2)) + tree_c = np.full((int(nc / 2) * 2), None) + + for k in range(int(nc / 2)): + + # Select Parents Indices + i1 = roulette_wheel_selection(P) + i2 = roulette_wheel_selection(P) + + # Select Parents + p1 = copy.deepcopy(position_ij[i1]) + p2 = copy.deepcopy(position_ij[i2]) + + # Perform Crossover + y1, y2 = crossover(p1, p2) + + tree, cost, cons = cost_function(y1, W, self.WindFarm, penalization, self.cables, n_wt, CoordX, CoordY) + position_cj[k, :] = y1 + cons_ck[k, :] = cons + cost_c[k] = cost + tree_c[k] = tree + + tree, cost, cons = cost_function(y2, W, self.WindFarm, penalization, self.cables, n_wt, CoordX, CoordY) + position_cj[int(k + nc / 2), :] = y2 + cons_ck[int(k + nc / 2), :] = cons + cost_c[int(k + nc / 2)] = cost + tree_c[int(k + nc / 2)] = tree + # %% + """ + m : number of mutations, nm (changes with outer loop) + f : mutation type (4 types of mutations) + j : number of variables, nVar + k : number of constraints (5 types of constraints) + """ + position_m1j = np.full((nm, nVar), False) # pop + cons_m1k = np.full((nm, 5), False) + cost_m1 = np.zeros(nm) + tree_m1 = np.full(nm, None) + + position_m2j = np.full((nm2, nVar), False) # pop + cons_m2k = np.full((nm2, 5), False) + cost_m2 = np.zeros(nm2) + tree_m2 = np.full(nm2, None) + + position_m3j = np.full((nm3, nVar), False) # pop + cons_m3k = np.full((nm3, 5), False) + cost_m3 = np.zeros(nm3) + tree_m3 = np.full(nm3, None) + + position_m4j = np.full((nm4, nVar), False) # pop + cons_m4k = np.full((nm4, 5), False) + cost_m4 = np.zeros(nm4) + tree_m4 = np.full(nm4, None) + + for k in range(nm): + index = np.random.randint(0, nPop) + + # Perform Mutation + pom1 = copy.deepcopy(position_ij[index]) + xm1 = mutate(pom1, mu) + + tree, cost, cons = cost_function(xm1, W, self.WindFarm, penalization, self.cables, n_wt, CoordX, CoordY) + position_m1j[k, :] = xm1 + cons_m1k[k, :] = cons + cost_m1[k] = cost + tree_m1[k] = tree + + for k in range(nm2): + index = np.random.randint(0, nPop) + + # Perform Mutation + pom2 = copy.deepcopy(position_ij[index]) + xm2 = mutate(pom2, mu2) + + tree, cost, cons = cost_function(xm2, W, self.WindFarm, penalization, self.cables, n_wt, CoordX, CoordY) + position_m2j[k, :] = xm2 + cons_m2k[k, :] = cons + cost_m2[k] = cost + tree_m2[k] = tree + + for k in range(nm3): + index = np.random.randint(0, nPop) + + # Perform Mutation + pom3 = copy.deepcopy(position_ij[index]) + xm3 = mutate(pom3, mu3) + + tree, cost, cons = cost_function(xm3, W, self.WindFarm, penalization, self.cables, n_wt, CoordX, CoordY) + position_m3j[k, :] = xm3 + cons_m3k[k, :] = cons + cost_m3[k] = cost + tree_m3[k] = tree + for k in range(nm4): + index = np.random.randint(0, nPop) + + # Perform Mutation + pom4 = copy.deepcopy(position_ij[index]) + xm4 = mutate(pom4, mu4) + + tree, cost, cons = cost_function(xm4, W, self.WindFarm, penalization, self.cables, n_wt, CoordX, CoordY) + position_m4j[k, :] = xm4 + cons_m4k[k, :] = cons + cost_m4[k] = cost + tree_m4[k] = tree + + position_ij = np.vstack([position_ij, position_cj, position_m1j, position_m2j, position_m3j, position_m4j]) + cons_ik = np.vstack([cons_ik, cons_ck, cons_m1k, cons_m2k, cons_m3k, cons_m4k]) + cost_i = np.vstack([cost_i[:, na], cost_c[:, na], cost_m1[:, na], cost_m2[:, na], cost_m3[:, na], cost_m4[:, na]]).ravel() + tree_i = np.vstack([tree_i[:, na], tree_c[:, na], tree_m1[:, na], tree_m2[:, na], tree_m3[:, na], tree_m4[:, na]]).ravel() + + sort_index = cost_i.argsort() + position_ij, cons_ik, cost_i, tree_i = position_ij[sort_index, :], cons_ik[sort_index, :], cost_i[sort_index], tree_i[sort_index] + + worst_cost = max(worst_cost, cost_i[-1]) + position_ij, cons_ik, cost_i, tree_i = position_ij[:nPop, :], cons_ik[:nPop, :], cost_i[:nPop], tree_i[:nPop] + termination_cost.append(cost_i[0]) + if self.verbose: + print(it) + print(cons_ik[0]) + print(cost_i[0]) + self.tree_i = tree_i + state = {'pos': position_ij[0, :], + 'cons': cons_ik[0, :], + 'cost': cost_i[0], + 'tree': tree_i[0], + 'edges': W[position_ij[0, :]][:, 0:2]} + if len(termination_cost) > StallIt and (len(set(termination_cost[-(StallIt + 1):])) == 1 and (cons_ik[0]).all()): + break + self.state = state + + # self.plot(CoordX, CoordY, tree_i) + # plt.show() + + H = G_from_T(state['tree'], self.G) + + return H + + +def G_from_T(T, G_base): + '''Creates a networkx graph with nodes and data from G_base and edges from + a T matrix. (suitable for converting the output of juru's `global_optimizer`) + T matrix: [ [u, v, length, cable type, load (WT number), cost] ]''' + G = G_base # nx.Graph() + T = np.insert(T, 4, 1, axis=1) + # G.graph.update(G_base.graph) + # G.add_nodes_from(G_base.nodes(data=True)) + M = G_base.graph['M'] + N = G_base.number_of_nodes() - M + + # indexing differences: + # T starts at 1, while G starts at -M + edges = (T[:, :2].astype(int) - M - 1) + + G.add_edges_from(edges) + nx.set_edge_attributes( + G, {(int(u), int(v)): dict(length=length, cable=cable, load=load, cost=cost) + for (u, v), length, (cable, load), cost in + zip(edges, T[:, 2], T[:, 3:5].astype(int), T[:, 5])}) + G.graph['has_loads'] = True + G.graph['has_costs'] = True + G.graph['edges_created_by'] = 'G_from_T()' + max_capacity = 0 + for u, v, cable_type in G.edges(data='cable'): + capacity_cable = G.graph['cables']['capacity'][cable_type] # Get the capacity for the current cable type + capacity = max(max_capacity, capacity_cable) # Update max_capacity if capacity is greater + + G.graph['capacity'] = capacity + G.graph['overfed'] = [len(G[root]) / np.ceil(N / capacity) * M + for root in range(-M, 0)] + return G + + +if __name__ == '__main__': + turbines_pos = np.array([[492039.7611, 490820.2714, 489321.8153, 488102.3256, + 486603.8695, 497753.2809, 496533.7913, 495035.3352, + 493815.8455, 492317.3894, 489599.4436, 498030.9093, + 496811.4196, 495312.9635], + [5731802.533, 5732985.596, 5732184.707, 5733367.77, + 5732566.881, 5733012.62, 5734195.683, 5733394.794, + 5734577.858, 5733776.968, 5734159.143, 5734987.056, + 5736170.119, 5735369.23]]).T + + substations_pos = np.array([[497620.7], [5730622.0]]).T + M = substations_pos.shape[0] + VertexC = np.r_[turbines_pos, substations_pos[::-1]] + N = len(VertexC) - M + + # Set default site information if not provided + BoundaryC = np.array(list(zip([484178.55, 500129.9, 497318.1, + 503163.37, 501266.5, 488951.0], + [5732482.8, 5737534.4, 5731880.24, + 5729155.3, 5715990.05, 5727940.]))) + site_info = {'site_name': 'IEA-37 Regular', + 'handle': 'iea37reg', + 'boundary': BoundaryC + } + site_info.setdefault('handle', 'G_from_site') + site_info.setdefault('name', site_info.get('handle')) + + # Create the network graph + G = nx.Graph( + M=M, + VertexC=VertexC, + boundary=site_info.get('boundary'), + name=site_info['name'], + handle=site_info['handle'], + cables=Cables() + ) + ecsga = GA(G) + plt.close('all') + # cable_cost, state = ecsga.run() + ecsga.plot() + plt.show() + """ + for i in range(5): + cable_cost, state = ecsga.run(x, y, x0, y0, ecsga.state) + x[np.random.randint(0, len(x) - 1)] *= (100 + np.random.randint(-5, 5)) / 100 + y[np.random.randint(0, len(y) - 1)] *= (100 + np.random.randint(-5, 5)) / 100 + print(i, cable_cost) + plt.figure(i) + ecsga.plot() + plt.show() + """ diff --git a/ed_win/drivers/ga/Mutate.py b/ed_win/drivers/ga/Mutate.py new file mode 100644 index 0000000..2a764c8 --- /dev/null +++ b/ed_win/drivers/ga/Mutate.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Feb 14 14:00:05 2020 + +@author: juru +""" +import numpy as np + + +def mutate(x, mu): + nVar = x.size + nmu = np.ceil(mu * nVar).astype(int) + j = np.random.choice(nVar, nmu) + y = x + y[j] = 1 - x[j] + y = y.astype(bool) + return y + + +if __name__ == '__main__': + x = np.array([False, False, True, False, False, False, False, True, True, + False, False, True, True, True, True, True, True, True, + True, True, False, False, False, False, False, True, False, + True, True, False, False, False, False, False, False, True, + True, False, False, True, False, True, True, False, True, + True, True, False, False, False, False, True, False, True, + True, False, True, True, True, False, True, False, False, + False, True, False, False, False, True, False, False, False, + True, True, False, True, False, True, False, False, True, + False, True, False, True, True, False]) + mu = 0.022988505747126436 + print(mutate(x, mu)) diff --git a/ed_win/drivers/ga/RouletteWheelSelection.py b/ed_win/drivers/ga/RouletteWheelSelection.py new file mode 100644 index 0000000..eaf48c4 --- /dev/null +++ b/ed_win/drivers/ga/RouletteWheelSelection.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Feb 12 13:33:23 2020 + +@author: juru +""" +import numpy as np +import scipy.io + + +def roulette_wheel_selection(P): + # r = np.random.rand(1) + # r = float(input('Insert r: ')) + + r = np.random.rand(1) + c = np.cumsum(P) + i = np.argwhere(r <= c)[0][0] +# print(r) +# print(i) + return i + + +if __name__ == '__main__': + P = np.array([1.08689101e-02, 1.08689101e-02, 1.08689100e-02, 1.08689100e-02, + 1.08689098e-02, 1.08689097e-02, 1.08689097e-02, 1.08689097e-02, + 1.08689097e-02, 1.08689097e-02, 1.08689097e-02, 1.08689096e-02, + 1.08689096e-02, 1.08689096e-02, 1.08689096e-02, 1.08689096e-02, + 1.08689096e-02, 1.08689095e-02, 1.08689095e-02, 1.08689095e-02, + 1.08689094e-02, 1.08689094e-02, 1.08689094e-02, 1.08689094e-02, + 1.08689094e-02, 1.08689094e-02, 1.08689094e-02, 1.08689094e-02, + 1.08689093e-02, 1.08689093e-02, 1.08689092e-02, 1.08689092e-02, + 1.08689092e-02, 1.08689092e-02, 1.08689092e-02, 1.08689092e-02, + 1.08689092e-02, 1.08689092e-02, 1.08689092e-02, 1.08689092e-02, + 1.08689091e-02, 1.08689091e-02, 1.08689091e-02, 1.08689091e-02, + 1.08689091e-02, 1.08689090e-02, 1.08689090e-02, 1.08689090e-02, + 1.08689090e-02, 1.08689089e-02, 1.08689089e-02, 1.08689089e-02, + 1.08689089e-02, 1.08689089e-02, 1.08689089e-02, 1.08689089e-02, + 1.08689089e-02, 1.08689089e-02, 1.08689089e-02, 1.08689089e-02, + 1.08689088e-02, 1.08689088e-02, 1.08689088e-02, 1.08689088e-02, + 1.08689088e-02, 1.08689088e-02, 1.08689088e-02, 1.08689088e-02, + 1.08689088e-02, 1.08689088e-02, 1.08689088e-02, 1.08689088e-02, + 1.08689088e-02, 1.08689088e-02, 1.08689087e-02, 1.08689087e-02, + 1.08689087e-02, 1.08689087e-02, 1.08689087e-02, 1.08689087e-02, + 1.08689086e-02, 1.08689086e-02, 1.08689086e-02, 1.08689086e-02, + 1.08689086e-02, 1.08689085e-02, 1.08689085e-02, 1.08689085e-02, + 1.08689085e-02, 1.08689083e-02, 1.08689082e-02, 1.08689082e-02, + 7.54537492e-06, 7.54537467e-06, 7.54537462e-06, 7.54537449e-06, + 7.54537439e-06, 7.54537432e-06, 7.54537431e-06, 7.54537400e-06]) + + print(roulette_wheel_selection(P)) + 0.640305652380855 + 0.880640348452745 diff --git a/ed_win/drivers/ga/TwoLinesIntersecting.py b/ed_win/drivers/ga/TwoLinesIntersecting.py new file mode 100644 index 0000000..f607bba --- /dev/null +++ b/ed_win/drivers/ga/TwoLinesIntersecting.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Feb 11 13:33:46 2020 + +@author: juru +""" + +import numpy as np +# import + + +def two_lines_intersecting(line1, line2): + """ + """ + with np.errstate(divide='ignore', invalid='ignore'): + x1 = [line1[0][0], line1[1][0]] + y1 = [line1[0][1], line1[1][1]] + # plt.plot(x1, y1, label = "line 1") + x2 = [line2[0][0], line2[1][0]] + y2 = [line2[0][1], line2[1][1]] + # plt.plot(x2, y2, label = "line 2") + + m1 = np.true_divide((line1[1, 1] - line1[0, 1]), (line1[1, 0] - line1[0, 0])) + m2 = np.true_divide((line2[1, 1] - line2[0, 1]), (line2[1, 0] - line2[0, 0])) + b1 = line1[0, 1] - m1 * line1[0, 0] + b2 = line2[0, 1] - m2 * line2[0, 0] + + xintersect = np.true_divide((b2 - b1), (m1 - m2)) + yintersect = m1 * xintersect + b1 + + if (np.abs(m1 - m2) > 1e-6): + isPointInsidex1 = ( + ((xintersect - line1[0, 0]) > 1e-6 and (xintersect - line1[1, 0]) < -1e-6) or + ((xintersect - line1[1, 0]) > 1e-6 and (xintersect - line1[0, 0]) < -1e-6)) + + isPointInsidex2 = ( + ((xintersect - line2[0, 0]) > 1e-6 and (xintersect - line2[1, 0]) < -1e-6) or + ((xintersect - line2[1, 0]) > 1e-6 and (xintersect - line2[0, 0]) < -1e-6)) + + inside = isPointInsidex1 and isPointInsidex2 + intersect = inside + + if (np.abs(m1 - m2) < 1e-6): + if (np.abs(b1 - b2) > 1e-6): + intersect = False + if (np.abs(b1 - b2) < 1e-6): + isPointInside12 = (((line1[0, 0] - line2[0, 0]) > 1e-6 and + (line1[0, 0] - line2[1, 0]) < -1e-6) or + ((line1[0, 0] - line2[1, 0]) > 1e-6 and + (line1[0, 0] - line2[0, 0]) < -1e-6)) + + isPointInside22 = (((line1[1, 0] - line2[0, 0]) > 1e-6 and + (line1[1, 0] - line2[1, 0]) < -1e-6) or + ((line1[1, 0] - line2[1, 0]) > 1e-6 and + (line1[1, 0] - line2[0, 0]) < -1e-6)) + inside = isPointInside12 or isPointInside22 + intersect = inside + + if (((m1 == np.inf) or (m1 == -np.inf)) or ((m2 == np.inf) or (m2 == -np.inf))): + if (((m1 == np.inf) or (m1 == -np.inf)) or ((m2 == np.inf) or (m2 == -np.inf))): + if ((m1 != 0) and (m2 != 0)): + line3 = np.zeros((2, 2)) + line4 = np.zeros((2, 2)) + line3[0, 0] = line1[0, 1] + line3[0, 1] = line1[0, 0] + line3[1, 0] = line1[1, 1] + line3[1, 1] = line1[1, 0] + line4[0, 0] = line2[0, 1] + line4[0, 1] = line2[0, 0] + line4[1, 0] = line2[1, 1] + line4[1, 1] = line2[1, 0] + m3 = (line3[1, 1] - line3[0, 1]) / (line3[1, 0] - line3[0, 0]) + m4 = (line4[1, 1] - line4[0, 1]) / (line4[1, 0] - line4[0, 0]) + b3 = line3[0, 1] - m3 * line3[0, 0] + b4 = line4[0, 1] - m4 * line4[0, 0] + xintersect2 = (b4 - b3) / (m3 - m4) + yintersect2 = m3 * xintersect2 + b3 + isPointInsidex6 = ( + ((xintersect2 - line3[0, 0]) > 1e-6 and (xintersect2 - line3[1, 0]) < -1e-6) or + ((xintersect2 - line3[1, 0]) > 1e-6 and (xintersect2 - line3[0, 0]) < -1e-6)) + isPointInsidex7 = ( + ((xintersect2 - line4[0, 0]) > 1e-6 and (xintersect2 - line4[1, 0]) < -1e-6) or + ((xintersect2 - line4[1, 0]) > 1e-6 and (xintersect2 - line4[0, 0]) < -1e-6)) + + inside = isPointInsidex6 and isPointInsidex7 + + intersect = inside + else: + if (m1 == 0): + y1 = line1[0, 1] + x1min = np.min((line1[0, 0], line1[1, 0])) + x1max = np.max((line1[0, 0], line1[1, 0])) + x2 = line2[0, 0] + y2min = np.min((line2[0, 1], line2[1, 1])) + y2max = np.max((line2[0, 1], line2[1, 1])) + if ((y1 > y2min) and (y1 < y2max) and (x2 > x1min) and (x2 < x1max)): + intersect = True + else: + intersect = False + if (m2 == 0): + y2 = line2[0, 1] + x2min = np.min((line2[0, 0], line2[1, 0])) + x2max = np.max((line2[0, 0], line2[1, 0])) + x1 = line1[0, 0] + y1min = np.min((line1[0, 1], line1[1, 1])) + y1max = np.max((line1[0, 1], line1[1, 1])) + if ((y2 > y1min) and (y2 < y1max) and (x1 > x2min) and (x1 < x2max)): + intersect = True + else: + intersect = False + if (((m1 == np.inf) or (m1 == -np.inf)) and ((m2 == np.inf) or (m2 == -np.inf))): + if (line1[0, 0] == line2[0, 0]): + insidet = (((line1[0, 1] - line2[0, 1]) > 1e-6 and (line1[0, 1] - line2[1, 1]) < -1e-6) or + ((line1[0, 1] - line2[1, 1]) > 1e-6 and (line1[0, 1] - line2[0, 1]) < -1e-6)) + insidep = (((line1[1, 1] - line2[0, 1]) > 1e-6 and (line1[1, 1] - line2[1, 1]) < -1e-6) or + ((line1[1, 1] - line2[1, 1]) > 1e-6 and (line1[1, 1] - line2[0, 1]) < -1e-6)) + inside = insidet or insidep + intersect = inside + if (line1[0, 0] != line2[0, 0]): + intersect = False + return intersect + + +if __name__ == '__main__': + line1 = np.array([[1, 2], [3, 4]]) # The first column represents to x-values of the line segment line[0,0] y line[1,0]. The second column represents the y-values of + # the line segment + line2 = np.array([[1, 4], [3, 2]]) + + line1 = np.random.rand(4).reshape((2, 2)) * 10 - 5 + line2 = np.random.rand(4).reshape((2, 2)) * 10 - 5 + +# line1=np.array([[1,2],[7,2]]) +# line2=np.array([[5,3],[5,1]]) + + intersect = two_lines_intersecting(line1, line2) + print(intersect) diff --git a/ed_win/drivers/ga/__init__.py b/ed_win/drivers/ga/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ed_win/drivers/ga/data.py b/ed_win/drivers/ga/data.py new file mode 100644 index 0000000..7dbe62c --- /dev/null +++ b/ed_win/drivers/ga/data.py @@ -0,0 +1,260 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu Jan 9 09:59:03 2020 + +@author: mikf +""" +import scipy.io +import numpy as np + + +class Mat2Py(): + def __init__(self, filename='../Diagonal20.mat', filetype='struct'): + if filetype == 'struct': + mat = scipy.io.loadmat(filename, struct_as_record=False) + self.n_wt = int(mat['WindFarm'].ravel()[0].Nwt[0][0]) + self.Edges = np.array(mat['WindFarm'].ravel()[0].Edges) + self.CoordX = np.array(mat['WindFarm'].ravel()[0].Coord.ravel()[0].x.ravel()) + self.CoordY = np.array(mat['WindFarm'].ravel()[0].Coord.ravel()[0].y.ravel()) + +# def mat2py(filename='../Diagonal20.mat', filetype='struct'): +# if filetype=='struct': +# mat = scipy.io.loadmat(filename,struct_as_record=False) +# n_wt = mat['WindFarm'].ravel()[0].Nwt[0][0] +# Edges = mat['WindFarm'].ravel()[0].Edges +# CoordX = mat['WindFarm'].ravel()[0].Coord.ravel()[0].x +# CoordY = mat['WindFarm'].ravel()[0].Coord.ravel()[0].y +# return n_wt, Edges, CoordX, CoordY +# +# n_wt, Edges, CoordX, CoordY = mat2py() + + +# +# +# +# +# def get_n_wt(): +# return np.array([[21]]) +# def get_edges(): +# Edges = np.array([[1.00000000e+00, 2.00000000e+00, 2.94340792e+03], +# [1.00000000e+00, 3.00000000e+00, 2.69656881e+03], +# [1.00000000e+00, 4.00000000e+00, 2.50257235e+03], +# [1.00000000e+00, 5.00000000e+00, 1.72254666e+03], +# [1.00000000e+00, 6.00000000e+00, 1.90398993e+03], +# [1.00000000e+00, 7.00000000e+00, 2.87000006e+03], +# [1.00000000e+00, 8.00000000e+00, 2.62769314e+03], +# [1.00000000e+00, 9.00000000e+00, 1.30596147e+03], +# [1.00000000e+00, 1.00000000e+01, 6.78241483e+02], +# [1.00000000e+00, 1.10000000e+01, 1.77994372e+03], +# [1.00000000e+00, 1.20000000e+01, 1.86724086e+03], +# [1.00000000e+00, 1.30000000e+01, 5.80973376e+02], +# [1.00000000e+00, 1.40000000e+01, 1.06872994e+03], +# [1.00000000e+00, 1.50000000e+01, 2.42910463e+03], +# [1.00000000e+00, 1.60000000e+01, 2.88276364e+03], +# [1.00000000e+00, 1.70000000e+01, 1.80491920e+03], +# [1.00000000e+00, 1.80000000e+01, 1.46930339e+03], +# [1.00000000e+00, 1.90000000e+01, 2.23912562e+03], +# [1.00000000e+00, 2.00000000e+01, 2.49363307e+03], +# [1.00000000e+00, 2.10000000e+01, 2.67759267e+03], +# [2.00000000e+00, 3.00000000e+00, 1.40600711e+03], +# [2.00000000e+00, 4.00000000e+00, 1.23342708e+03], +# [2.00000000e+00, 5.00000000e+00, 1.26280000e+03], +# [2.00000000e+00, 6.00000000e+00, 2.37101188e+03], +# [2.00000000e+00, 7.00000000e+00, 3.68814897e+03], +# [2.00000000e+00, 8.00000000e+00, 2.46685416e+03], +# [2.00000000e+00, 9.00000000e+00, 2.06280304e+03], +# [2.00000000e+00, 1.00000000e+01, 2.52560000e+03], +# [2.00000000e+00, 1.10000000e+01, 3.52928694e+03], +# [2.00000000e+00, 1.20000000e+01, 3.15568276e+03], +# [2.00000000e+00, 1.30000000e+01, 3.19034451e+03], +# [2.00000000e+00, 1.40000000e+01, 3.78840000e+03], +# [2.00000000e+00, 1.50000000e+01, 4.74124066e+03], +# [2.00000000e+00, 1.60000000e+01, 4.32483476e+03], +# [2.00000000e+00, 1.70000000e+01, 4.12560609e+03], +# [2.00000000e+00, 1.80000000e+01, 4.39212563e+03], +# [2.00000000e+00, 1.90000000e+01, 5.05120000e+03], +# [2.00000000e+00, 2.00000000e+01, 5.22228352e+03], +# [2.00000000e+00, 2.10000000e+01, 5.62072634e+03], +# [3.00000000e+00, 4.00000000e+00, 2.32416283e+03], +# [3.00000000e+00, 5.00000000e+00, 1.23342708e+03], +# [3.00000000e+00, 6.00000000e+00, 1.26280000e+03], +# [3.00000000e+00, 7.00000000e+00, 2.37101188e+03], +# [3.00000000e+00, 8.00000000e+00, 3.44518424e+03], +# [3.00000000e+00, 9.00000000e+00, 2.46685416e+03], +# [3.00000000e+00, 1.00000000e+01, 2.06280304e+03], +# [3.00000000e+00, 1.10000000e+01, 2.52560000e+03], +# [3.00000000e+00, 1.20000000e+01, 3.70028124e+03], +# [3.00000000e+00, 1.30000000e+01, 3.15568276e+03], +# [3.00000000e+00, 1.40000000e+01, 3.19034451e+03], +# [3.00000000e+00, 1.50000000e+01, 3.78840000e+03], +# [3.00000000e+00, 1.60000000e+01, 4.93370831e+03], +# [3.00000000e+00, 1.70000000e+01, 4.32483476e+03], +# [3.00000000e+00, 1.80000000e+01, 4.12560609e+03], +# [3.00000000e+00, 1.90000000e+01, 4.39212563e+03], +# [3.00000000e+00, 2.00000000e+01, 5.18706447e+03], +# [3.00000000e+00, 2.10000000e+01, 5.22228352e+03], +# [4.00000000e+00, 5.00000000e+00, 1.40600711e+03], +# [4.00000000e+00, 6.00000000e+00, 2.81201422e+03], +# [4.00000000e+00, 7.00000000e+00, 4.21802134e+03], +# [4.00000000e+00, 8.00000000e+00, 1.23342708e+03], +# [4.00000000e+00, 9.00000000e+00, 1.26280000e+03], +# [4.00000000e+00, 1.00000000e+01, 2.37101188e+03], +# [4.00000000e+00, 1.10000000e+01, 3.68814897e+03], +# [4.00000000e+00, 1.20000000e+01, 2.06280304e+03], +# [4.00000000e+00, 1.30000000e+01, 2.52560000e+03], +# [4.00000000e+00, 1.40000000e+01, 3.52928694e+03], +# [4.00000000e+00, 1.50000000e+01, 4.74202375e+03], +# [4.00000000e+00, 1.60000000e+01, 3.15568276e+03], +# [4.00000000e+00, 1.70000000e+01, 3.19034451e+03], +# [4.00000000e+00, 1.80000000e+01, 3.78840000e+03], +# [4.00000000e+00, 1.90000000e+01, 4.74124066e+03], +# [4.00000000e+00, 2.00000000e+01, 4.39212563e+03], +# [4.00000000e+00, 2.10000000e+01, 5.05120000e+03], +# [5.00000000e+00, 6.00000000e+00, 1.40600711e+03], +# [5.00000000e+00, 7.00000000e+00, 2.81201422e+03], +# [5.00000000e+00, 8.00000000e+00, 2.32416283e+03], +# [5.00000000e+00, 9.00000000e+00, 1.23342708e+03], +# [5.00000000e+00, 1.00000000e+01, 1.26280000e+03], +# [5.00000000e+00, 1.10000000e+01, 2.37101188e+03], +# [5.00000000e+00, 1.20000000e+01, 2.46685416e+03], +# [5.00000000e+00, 1.30000000e+01, 2.06280304e+03], +# [5.00000000e+00, 1.40000000e+01, 2.52560000e+03], +# [5.00000000e+00, 1.50000000e+01, 3.52928694e+03], +# [5.00000000e+00, 1.60000000e+01, 3.70028124e+03], +# [5.00000000e+00, 1.70000000e+01, 3.15568276e+03], +# [5.00000000e+00, 1.80000000e+01, 3.19034451e+03], +# [5.00000000e+00, 1.90000000e+01, 3.78840000e+03], +# [5.00000000e+00, 2.00000000e+01, 4.12560609e+03], +# [5.00000000e+00, 2.10000000e+01, 4.39212563e+03], +# [6.00000000e+00, 7.00000000e+00, 1.40600711e+03], +# [6.00000000e+00, 8.00000000e+00, 3.63810877e+03], +# [6.00000000e+00, 9.00000000e+00, 2.32416283e+03], +# [6.00000000e+00, 1.00000000e+01, 1.23342708e+03], +# [6.00000000e+00, 1.10000000e+01, 1.26280000e+03], +# [6.00000000e+00, 1.20000000e+01, 3.44518424e+03], +# [6.00000000e+00, 1.30000000e+01, 2.46685416e+03], +# [6.00000000e+00, 1.40000000e+01, 2.06280304e+03], +# [6.00000000e+00, 1.50000000e+01, 2.52560000e+03], +# [6.00000000e+00, 1.60000000e+01, 4.62380155e+03], +# [6.00000000e+00, 1.70000000e+01, 3.70028124e+03], +# [6.00000000e+00, 1.80000000e+01, 3.15568276e+03], +# [6.00000000e+00, 1.90000000e+01, 3.19034451e+03], +# [6.00000000e+00, 2.00000000e+01, 4.32483476e+03], +# [6.00000000e+00, 2.10000000e+01, 4.12560609e+03], +# [7.00000000e+00, 8.00000000e+00, 5.00236443e+03], +# [7.00000000e+00, 9.00000000e+00, 3.63810877e+03], +# [7.00000000e+00, 1.00000000e+01, 2.32416283e+03], +# [7.00000000e+00, 1.10000000e+01, 1.23342708e+03], +# [7.00000000e+00, 1.20000000e+01, 4.64832567e+03], +# [7.00000000e+00, 1.30000000e+01, 3.44518424e+03], +# [7.00000000e+00, 1.40000000e+01, 2.46685416e+03], +# [7.00000000e+00, 1.50000000e+01, 2.06280304e+03], +# [7.00000000e+00, 1.60000000e+01, 5.74636514e+03], +# [7.00000000e+00, 1.70000000e+01, 4.62380155e+03], +# [7.00000000e+00, 1.80000000e+01, 3.70028124e+03], +# [7.00000000e+00, 1.90000000e+01, 3.15568276e+03], +# [7.00000000e+00, 2.00000000e+01, 4.93370831e+03], +# [7.00000000e+00, 2.10000000e+01, 4.32483476e+03], +# [8.00000000e+00, 9.00000000e+00, 1.40600711e+03], +# [8.00000000e+00, 1.00000000e+01, 2.81201422e+03], +# [8.00000000e+00, 1.10000000e+01, 4.21802134e+03], +# [8.00000000e+00, 1.20000000e+01, 1.26280000e+03], +# [8.00000000e+00, 1.30000000e+01, 2.37101188e+03], +# [8.00000000e+00, 1.40000000e+01, 3.68814897e+03], +# [8.00000000e+00, 1.50000000e+01, 5.05340482e+03], +# [8.00000000e+00, 1.60000000e+01, 2.06280304e+03], +# [8.00000000e+00, 1.70000000e+01, 2.52560000e+03], +# [8.00000000e+00, 1.80000000e+01, 3.52928694e+03], +# [8.00000000e+00, 1.90000000e+01, 4.74202375e+03], +# [8.00000000e+00, 2.00000000e+01, 3.78840000e+03], +# [8.00000000e+00, 2.10000000e+01, 4.74124066e+03], +# [9.00000000e+00, 1.00000000e+01, 1.40600711e+03], +# [9.00000000e+00, 1.10000000e+01, 2.81201422e+03], +# [9.00000000e+00, 1.20000000e+01, 1.23342708e+03], +# [9.00000000e+00, 1.30000000e+01, 1.26280000e+03], +# [9.00000000e+00, 1.40000000e+01, 2.37101188e+03], +# [9.00000000e+00, 1.50000000e+01, 3.68814897e+03], +# [9.00000000e+00, 1.60000000e+01, 2.46685416e+03], +# [9.00000000e+00, 1.70000000e+01, 2.06280304e+03], +# [9.00000000e+00, 1.80000000e+01, 2.52560000e+03], +# [9.00000000e+00, 1.90000000e+01, 3.52928694e+03], +# [9.00000000e+00, 2.00000000e+01, 3.19034451e+03], +# [9.00000000e+00, 2.10000000e+01, 3.78840000e+03], +# [1.00000000e+01, 1.10000000e+01, 1.40600711e+03], +# [1.00000000e+01, 1.20000000e+01, 2.32416283e+03], +# [1.00000000e+01, 1.30000000e+01, 1.23342708e+03], +# [1.00000000e+01, 1.40000000e+01, 1.26280000e+03], +# [1.00000000e+01, 1.50000000e+01, 2.37101188e+03], +# [1.00000000e+01, 1.60000000e+01, 3.44518424e+03], +# [1.00000000e+01, 1.70000000e+01, 2.46685416e+03], +# [1.00000000e+01, 1.80000000e+01, 2.06280304e+03], +# [1.00000000e+01, 1.90000000e+01, 2.52560000e+03], +# [1.00000000e+01, 2.00000000e+01, 3.15568276e+03], +# [1.00000000e+01, 2.10000000e+01, 3.19034451e+03], +# [1.10000000e+01, 1.20000000e+01, 3.63810877e+03], +# [1.10000000e+01, 1.30000000e+01, 2.32416283e+03], +# [1.10000000e+01, 1.40000000e+01, 1.23342708e+03], +# [1.10000000e+01, 1.50000000e+01, 1.26280000e+03], +# [1.10000000e+01, 1.60000000e+01, 4.64832567e+03], +# [1.10000000e+01, 1.70000000e+01, 3.44518424e+03], +# [1.10000000e+01, 1.80000000e+01, 2.46685416e+03], +# [1.10000000e+01, 1.90000000e+01, 2.06280304e+03], +# [1.10000000e+01, 2.00000000e+01, 3.70028124e+03], +# [1.10000000e+01, 2.10000000e+01, 3.15568276e+03], +# [1.20000000e+01, 1.30000000e+01, 1.40600711e+03], +# [1.20000000e+01, 1.40000000e+01, 2.81201422e+03], +# [1.20000000e+01, 1.50000000e+01, 4.21802134e+03], +# [1.20000000e+01, 1.60000000e+01, 1.23342708e+03], +# [1.20000000e+01, 1.70000000e+01, 1.26280000e+03], +# [1.20000000e+01, 1.80000000e+01, 2.37101188e+03], +# [1.20000000e+01, 1.90000000e+01, 3.68814897e+03], +# [1.20000000e+01, 2.00000000e+01, 2.52560000e+03], +# [1.20000000e+01, 2.10000000e+01, 3.52928694e+03], +# [1.30000000e+01, 1.40000000e+01, 1.40600711e+03], +# [1.30000000e+01, 1.50000000e+01, 2.81201422e+03], +# [1.30000000e+01, 1.60000000e+01, 2.32416283e+03], +# [1.30000000e+01, 1.70000000e+01, 1.23342708e+03], +# [1.30000000e+01, 1.80000000e+01, 1.26280000e+03], +# [1.30000000e+01, 1.90000000e+01, 2.37101188e+03], +# [1.30000000e+01, 2.00000000e+01, 2.06280304e+03], +# [1.30000000e+01, 2.10000000e+01, 2.52560000e+03], +# [1.40000000e+01, 1.50000000e+01, 1.40600711e+03], +# [1.40000000e+01, 1.60000000e+01, 3.63810877e+03], +# [1.40000000e+01, 1.70000000e+01, 2.32416283e+03], +# [1.40000000e+01, 1.80000000e+01, 1.23342708e+03], +# [1.40000000e+01, 1.90000000e+01, 1.26280000e+03], +# [1.40000000e+01, 2.00000000e+01, 2.46685416e+03], +# [1.40000000e+01, 2.10000000e+01, 2.06280304e+03], +# [1.50000000e+01, 1.60000000e+01, 5.00236443e+03], +# [1.50000000e+01, 1.70000000e+01, 3.63810877e+03], +# [1.50000000e+01, 1.80000000e+01, 2.32416283e+03], +# [1.50000000e+01, 1.90000000e+01, 1.23342708e+03], +# [1.50000000e+01, 2.00000000e+01, 3.44518424e+03], +# [1.50000000e+01, 2.10000000e+01, 2.46685416e+03], +# [1.60000000e+01, 1.70000000e+01, 1.40600711e+03], +# [1.60000000e+01, 1.80000000e+01, 2.81201422e+03], +# [1.60000000e+01, 1.90000000e+01, 4.21802134e+03], +# [1.60000000e+01, 2.00000000e+01, 2.37101188e+03], +# [1.60000000e+01, 2.10000000e+01, 3.68814897e+03], +# [1.70000000e+01, 1.80000000e+01, 1.40600711e+03], +# [1.70000000e+01, 1.90000000e+01, 2.81201422e+03], +# [1.70000000e+01, 2.00000000e+01, 1.26280000e+03], +# [1.70000000e+01, 2.10000000e+01, 2.37101188e+03], +# [1.80000000e+01, 1.90000000e+01, 1.40600711e+03], +# [1.80000000e+01, 2.00000000e+01, 1.23342708e+03], +# [1.80000000e+01, 2.10000000e+01, 1.26280000e+03], +# [1.90000000e+01, 2.00000000e+01, 2.32416283e+03], +# [1.90000000e+01, 2.10000000e+01, 1.23342708e+03], +# [2.00000000e+01, 2.10000000e+01, 1.40600711e+03]]) +# return Edges +# def get_coordinates(): +# CoordX=np.array([10000,7113.10301762588,7924.86160242803,7564.14443282372,8375.90301762588,9187.66160242803,9999.42018723019,8015.18584802156,8826.94443282372,9638.70301762588,10450.4616024280,9277.98584802156,10089.7444328237,10901.5030176259,11713.2616024280,9729.02726321941,10540.7858480216,11352.5444328237,12164.3030176259,11803.5858480216,12615.3444328237]) +# CoordY=np.array([10000,9426,8278,10574,9426,8278,7130,11722,10574,9426,8278,11722,10574,9426,8278,12870,11722,10574,9426,11722,10574]) +# return CoordX,CoordY +# +# CoordX,CoordY = get_coordinates() +# data = Mat2Py() +# CoordX2, CoordY2 = data.CoordX, data.CoordY +# +# Edges = get_edges() +# Edges2 = data.Edges diff --git a/ed_win/drivers/ga/dict_to_class.py b/ed_win/drivers/ga/dict_to_class.py new file mode 100644 index 0000000..320ec91 --- /dev/null +++ b/ed_win/drivers/ga/dict_to_class.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Feb 18 09:27:16 2020 + +@author: mikf +""" + + +class DictToClass(): + def __init__(self, dic): + for k, v in dic.items(): + setattr(self, k, v) + + def _get_variables(self): + x = filter(lambda x: not x.startswith('_'), dir(self)) + return x + + +if __name__ == '__main__': + dic = {'a': 4, 'b': 345} + Dic = DictToClass(dic) diff --git a/ed_win/drivers/interarray b/ed_win/drivers/interarray new file mode 160000 index 0000000..7d6b1fd --- /dev/null +++ b/ed_win/drivers/interarray @@ -0,0 +1 @@ +Subproject commit 7d6b1fd410a48b925cdc998f00504ff324a4e202 diff --git a/ed_win/c_mst.py b/ed_win/drivers/tsh/c_mst.py similarity index 96% rename from ed_win/c_mst.py rename to ed_win/drivers/tsh/c_mst.py index 95a8569..4a7de51 100644 --- a/ed_win/c_mst.py +++ b/ed_win/drivers/tsh/c_mst.py @@ -67,13 +67,13 @@ def capacitated_spanning_tree(X=[], Y=[], option=3, UL=100, Inters_const=True, m mst_edges = np.zeros(2 * half, dtype=bool) # Array containing the activation variables of selected edges feasible = False # %% Main (until line 609 .m file) + max_value_potential_edge = 10**49 while go: flag1, flag2, flag3, flag4 = True, True, True, True it += 1 value_potential_edge, pos_potential_edge = np.min(edges_tot[:, 4]), np.argmin(edges_tot[:, 4]) - if (value_potential_edge > 10**49) or (it == max_it): # Condition to stop if a C-MST cannot be found - # print(it) - # print(value_potential_edge) + # if (value_potential_edge > max_value_potential_edge) or (it == max_it): # Condition to stop if a C-MST cannot be found + if (it == max_it): # Condition to stop if a C-MST cannot be found break node1, node2 = edges_tot[pos_potential_edge, 0].astype(int), edges_tot[pos_potential_edge, 1].astype(int) if (CP[node1 - 1] == CP[node2 - 1]) and (flag1) and (flag2) and (flag3) and (flag4): # Condition for avoiding the creation of loops @@ -96,7 +96,7 @@ def capacitated_spanning_tree(X=[], Y=[], option=3, UL=100, Inters_const=True, m edges_tot[pos_potential_edge, 4] = edges_tot[pos_potential_edge, 2] + 10**50 edges_tot[pos_potential_edge - half, 4] = edges_tot[pos_potential_edge - half, 2] + 10**50 else: # If capacity constraint not violated, then evaluate no-crossing cables constraint - if (not(intersection_checker(pos_potential_edge, edges_tot, mst_edges, X, Y, Inters_const))): # If no cables crossing, add the edge to the tree + if (not (intersection_checker(pos_potential_edge, edges_tot, mst_edges, X, Y, Inters_const))): # If no cables crossing, add the edge to the tree mst_edges[pos_potential_edge] = True # Add it to the tree. line 88 .m file # Update node address address_nodes[node2 - 1] = 1 @@ -159,7 +159,7 @@ def capacitated_spanning_tree(X=[], Y=[], option=3, UL=100, Inters_const=True, m edges_tot[pos_potential_edge, 4] = edges_tot[pos_potential_edge, 2] + 10**50 edges_tot[pos_potential_edge - half, 4] = edges_tot[pos_potential_edge - half, 2] + 10**50 else: - if (not(intersection_checker(pos_potential_edge, edges_tot, mst_edges, X, Y, Inters_const))): # If no cables crossing, add the edge to the tree + if (not (intersection_checker(pos_potential_edge, edges_tot, mst_edges, X, Y, Inters_const))): # If no cables crossing, add the edge to the tree mst_edges[pos_potential_edge] = True # Add it to the tree. line 190 .m file # Update node address address_nodes[node1 - 1] = 1 @@ -230,7 +230,7 @@ def capacitated_spanning_tree(X=[], Y=[], option=3, UL=100, Inters_const=True, m edges_tot[pos_potential_edge, 4] = edges_tot[pos_potential_edge, 2] + 10**50 edges_tot[pos_potential_edge - half, 4] = edges_tot[pos_potential_edge - half, 2] + 10**50 else: # No violation of capacity constraint - if (not(intersection_checker(pos_potential_edge, edges_tot, mst_edges, X, Y, Inters_const))): # If no cables crossing, add the edge to the tree + if (not (intersection_checker(pos_potential_edge, edges_tot, mst_edges, X, Y, Inters_const))): # If no cables crossing, add the edge to the tree mst_edges[pos_potential_edge] = True # Add it to the tree. line 301 .m file # Update node address if address_nodes[node1 - 1] == 1: @@ -298,7 +298,7 @@ def capacitated_spanning_tree(X=[], Y=[], option=3, UL=100, Inters_const=True, m edges_tot[pos_potential_edge, 4] = edges_tot[pos_potential_edge, 2] + 10**50 edges_tot[pos_potential_edge - half, 4] = edges_tot[pos_potential_edge - half, 2] + 10**50 else: # No violation of capacity constraint - if (not(intersection_checker(pos_potential_edge, edges_tot, mst_edges, X, Y, Inters_const))): # If no cables crossing, add the edge to the tree + if (not (intersection_checker(pos_potential_edge, edges_tot, mst_edges, X, Y, Inters_const))): # If no cables crossing, add the edge to the tree mst_edges[pos_potential_edge] = True # Add it to the tree. line 413 .m file # Update node address if address_nodes[node2 - 1] == 1: @@ -363,7 +363,7 @@ def capacitated_spanning_tree(X=[], Y=[], option=3, UL=100, Inters_const=True, m edges_tot[pos_potential_edge, 4] = edges_tot[pos_potential_edge, 2] + 10**50 edges_tot[pos_potential_edge - half, 4] = edges_tot[pos_potential_edge - half, 2] + 10**50 else: # If no violation of the capacity constraint - if (not(intersection_checker(pos_potential_edge, edges_tot, mst_edges, X, Y, Inters_const))): # If no cables crossing, add the edge to the tree + if (not (intersection_checker(pos_potential_edge, edges_tot, mst_edges, X, Y, Inters_const))): # If no cables crossing, add the edge to the tree mst_edges[pos_potential_edge] = True # Add it to the tree. line 522 .m file # Update weights and cost functions if option == 1: @@ -416,6 +416,12 @@ def capacitated_spanning_tree(X=[], Y=[], option=3, UL=100, Inters_const=True, m T[:, 0] = edges_tot[np.where(mst_edges)[0], 0] T[:, 1] = edges_tot[np.where(mst_edges)[0], 1] T[:, 2] = edges_tot[np.where(mst_edges)[0], 2] + + print('iteration:', it) + print('value_potential_edge:', value_potential_edge) + print('max_value_potential_edge:', max_value_potential_edge) + print('maximum iteration:', max_it) + max_it = 50000 # %% Running the function return T, feasible diff --git a/ed_win/c_mst_cables.py b/ed_win/drivers/tsh/c_mst_cables.py similarity index 90% rename from ed_win/c_mst_cables.py rename to ed_win/drivers/tsh/c_mst_cables.py index 461b208..4f174b3 100644 --- a/ed_win/c_mst_cables.py +++ b/ed_win/drivers/tsh/c_mst_cables.py @@ -1,112 +1,115 @@ -# -*- coding: utf-8 -*- -""" -Created on Thu Jun 4 08:07:37 2020 - -@author: juru -""" - -import numpy as np -import networkx as nx -import matplotlib.pyplot as plt -import time -from ed_win.c_mst import capacitated_spanning_tree - - -def cmst_cables(X=[], Y=[], T=[], Cables=[], plot=False): - """ - Assigns cables to the obtained C-MST in previous stage - - Parameters - ---------- - *X, Y: Arrays([n_wt_oss]) - X,Y positions of the wind turbines and oss - *T: Obtained tree in previous stage - *Cables: Array([cables available, 3]). Each row is a cable available. Column number one is cross-section, column number 2 number of WTs - and column number 3 is the price/km of the cable - - :return: Td: Array. Each row is a connection. First column is node 1, second column node 2, third column length (m), fourd column is - cable type (index of the Cables array), and last column is the cost of the edge - - """ - G = nx.Graph() - G.add_nodes_from([x + 1 for x in range(len(T[:, 1]) + 1)]) - G.add_edges_from([tuple(T[edge, 0:2]) for edge in range(len(T[:, 1]))]) - T_d = np.array([x for x in nx.dfs_edges(G, source=1)]).astype(int) - accumulator = np.zeros(T_d.shape[0]) - for j in range(len(T[:, 1])): - k = j + 2 - continue_ite = 1 - look_up = k - while continue_ite: - accumulator += (T_d[:, 1] == look_up).astype(int) - if (T_d[:, 1] == look_up).astype(int).sum() > 1: - Exception('Error') - if T_d[(T_d[:, 1] == look_up)][0, 0] == 1: - continue_ite = 0 - else: - look_up = T_d[(T_d[:, 1] == look_up)][0, 0] - T_d = np.append(T_d, np.zeros((accumulator.shape[0], 3)), axis=1) - for k in range(T_d.shape[0]): - aux1 = np.argwhere((T[:, 0] == T_d[k, 0]) & (T[:, 1] == T_d[k, 1])) - aux2 = np.argwhere((T[:, 1] == T_d[k, 0]) & (T[:, 0] == T_d[k, 1])) - if aux2.size == 0: - T_d[k, 2] = T[aux1, 2] - if aux1.size == 0: - T_d[k, 2] = T[aux2, 2] - if (aux2.size == 0) and (aux1.size == 0): - Exception('Error') - for k in range(accumulator.shape[0]): - for m in range(Cables.shape[0]): - if accumulator[k] <= Cables[m, 1]: - break - T_d[k, 3] = m - for k in range(T_d.shape[0]): - T_d[k, 4] = (T_d[k, 2] / 1000) * Cables[T_d.astype(int)[k, 3], 2] - if plot: - plot_network(X, Y, Cables, T_d) - return T_d - - -def plot_network(X, Y, Cables, T_d): - plt.figure() - plt.plot(X[1:], Y[1:], 'r+', markersize=10, label='Turbines') - plt.plot(X[0], Y[0], 'ro', markersize=10, label='OSS') - for i in range(len(X)): - plt.text(X[i] + 50, Y[i] + 50, str(i + 1)) - colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'bg', 'gr', 'rc', 'cm'] - for i in range(Cables.shape[0]): - index = T_d[:, 3] == i - if index.any(): - n1xs = X[T_d[index, 0].astype(int) - 1].ravel().T - n2xs = X[T_d[index, 1].astype(int) - 1].ravel().T - n1ys = Y[T_d[index, 0].astype(int) - 1].ravel().T - n2ys = Y[T_d[index, 1].astype(int) - 1].ravel().T - xs = np.vstack([n1xs, n2xs]) - ys = np.vstack([n1ys, n2ys]) - plt.plot(xs, ys, '{}'.format(colors[i])) - plt.plot([], [], '{}'.format(colors[i]), label='Cable: {} mm2'.format(Cables[i, 0])) - plt.legend() - - -if __name__ == "__main__": - t = time.time() - X = [387100, 383400, 383400, 383900, 383200, 383200, 383200, 383200, 383200, 383200, 383200, 383200, 383300, 384200, 384200, 384100, 384000, 383800, 383700, 383600, 383500, 383400, 383600, 384600, 385400, 386000, 386100, 386200, 386300, 386500, 386600, 386700, 386800, 386900, 387000, 387100, 387200, 383900, 387400, 387500, 387600, 387800, 387900, 388000, 387600, 386800, 385900, 385000, 384100, 384500, 384800, 385000, 385100, 385200, 385400, 385500, 385700, 385800, 385900, 385900, 385500, 385500, 386000, 386200, 386200, 384500, 386200, 386700, 386700, 386700, 384300, 384400, 384500, 384600, 384300, 384700, 384700, 384700, 385500, 384300, 384300] - Y = [6109500, 6103800, 6104700, 6105500, 6106700, 6107800, 6108600, 6109500, 6110500, 6111500, 6112400, 6113400, 6114000, 6114200, 6115100, 6115900, 6116700, 6118400, 6119200, 6120000, 6120800, 6121800, 6122400, 6122000, 6121700, 6121000, 6120000, 6119100, 6118100, 6117200, 6116200, 6115300, 6114300, 6113400, 6112400, 6111500, 6110700, 6117600, 6108900, 6108100, 6107400, 6106300, 6105200, 6104400, 6103600, 6103600, 6103500, 6103400, 6103400, 6104400, 6120400, 6119500, 6118400, 6117400, 6116500, 6115500, 6114600, 6113500, 6112500, 6111500, 6105400, 6104200, 6110400, 6109400, 6108400, 6121300, 6107500, 6106400, 6105300, 6104400, 6113300, 6112500, 6111600, 6110800, 6110100, 6109200, 6108400, 6107600, 6106500, 6106600, 6105000] - X = np.array(X) - Y = np.array(Y) - - option = 3 - UL = 11 - Inters_const = False - Cables = np.array([[500, 3, 100000], [800, 5, 150000], [1000, 10, 250000]]) - - T, feasible = capacitated_spanning_tree(X, Y, option, UL, Inters_const) - - print("The total length of the solution is {value:.2f} m".format(value=sum(T[:, 2]))) - print("Feasibility: {feasible1}".format(feasible1=feasible)) - - if feasible: - T_cables = cmst_cables(X, Y, T, Cables, plot=True) - print("The total cost of the system is {value:.2f} Euros".format(value=T_cables[:, -1].sum())) - elapsed = time.time() - t - print("The total time is {timep: .2f} s".format(timep=elapsed)) +# -*- coding: utf-8 -*- +""" +Created on Thu Jun 4 08:07:37 2020 + +@author: juru +""" + +import numpy as np +import networkx as nx +import matplotlib.pyplot as plt +import time +from ed_win.c_mst import capacitated_spanning_tree + + +def cmst_cables(X=[], Y=[], T=[], Cables=[], plot=False): + """ + Assigns cables to the obtained C-MST in previous stage + + Parameters + ---------- + *X, Y: Arrays([n_wt_oss]) + X,Y positions of the wind turbines and oss + *T: Obtained tree in previous stage + *Cables: Array([cables available, 3]). Each row is a cable available. Column number one is cross-section, column number 2 number of WTs + and column number 3 is the price/km of the cable + + :return: Td: Array. Each row is a connection. First column is node 1, second column node 2, third column length (m), fourd column is + cable type (index of the Cables array), and last column is the cost of the edge + + """ + G = nx.Graph() + G.add_nodes_from([x + 1 for x in range(len(T[:, 1]) + 1)]) + G.add_edges_from([tuple(T[edge, 0:2]) for edge in range(len(T[:, 1]))]) + T_d = np.array([x for x in nx.dfs_edges(G, source=1)]).astype(int) + accumulator = np.zeros(T_d.shape[0]) + for j in range(len(T[:, 1])): + k = j + 2 + continue_ite = 1 + look_up = k + while continue_ite: + accumulator += (T_d[:, 1] == look_up).astype(int) + if (T_d[:, 1] == look_up).astype(int).sum() > 1: + Exception('Error') + if T_d[(T_d[:, 1] == look_up)][0, 0] == 1: + continue_ite = 0 + else: + look_up = T_d[(T_d[:, 1] == look_up)][0, 0] + T_d = np.append(T_d, np.zeros((accumulator.shape[0], 3)), axis=1) + for k in range(T_d.shape[0]): + aux1 = np.argwhere((T[:, 0] == T_d[k, 0]) & (T[:, 1] == T_d[k, 1])) + aux2 = np.argwhere((T[:, 1] == T_d[k, 0]) & (T[:, 0] == T_d[k, 1])) + if aux2.size == 0: + T_d[k, 2] = T[aux1, 2] + if aux1.size == 0: + T_d[k, 2] = T[aux2, 2] + if (aux2.size == 0) and (aux1.size == 0): + Exception('Error') + for k in range(accumulator.shape[0]): + for m in range(Cables.shape[0]): + if accumulator[k] <= Cables[m, 1]: + break + T_d[k, 3] = m + for k in range(T_d.shape[0]): + T_d[k, 4] = (T_d[k, 2]) * Cables[T_d.astype(int)[k, 3], 2] + if plot: + plot_network(X, Y, Cables, T_d) + return T_d + + +def plot_network(X, Y, Cables, T_d): + fig, ax = plt.subplots() + ax.plot(X[1:], Y[1:], 'r+', markersize=10, label='WT') + ax.plot(X[0], Y[0], 'ro', markersize=10, label='OSS') + for i in range(len(X)): + ax.text(X[i] + 50, Y[i] + 50, str(i + 1)) + colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'bg', 'gr', 'rc', 'cm'] + for i in range(Cables.shape[0]): + index = T_d[:, 3] == i + if index.any(): + n1xs = X[T_d[index, 0].astype(int) - 1].ravel().T + n2xs = X[T_d[index, 1].astype(int) - 1].ravel().T + n1ys = Y[T_d[index, 0].astype(int) - 1].ravel().T + n2ys = Y[T_d[index, 1].astype(int) - 1].ravel().T + xs = np.vstack([n1xs, n2xs]) + ys = np.vstack([n1ys, n2ys]) + ax.plot(xs, ys, f'{colors[i]}') + ax.plot([], [], f'{colors[i]}', label=f'{Cables[i, 0]} mm²') + ax.legend() + ax.axis('off') + ax.set_aspect('equal') + return ax + + +if __name__ == "__main__": + t = time.time() + X = [387100, 383400, 383400, 383900, 383200, 383200, 383200, 383200, 383200, 383200, 383200, 383200, 383300, 384200, 384200, 384100, 384000, 383800, 383700, 383600, 383500, 383400, 383600, 384600, 385400, 386000, 386100, 386200, 386300, 386500, 386600, 386700, 386800, 386900, 387000, 387100, 387200, 383900, 387400, 387500, 387600, 387800, 387900, 388000, 387600, 386800, 385900, 385000, 384100, 384500, 384800, 385000, 385100, 385200, 385400, 385500, 385700, 385800, 385900, 385900, 385500, 385500, 386000, 386200, 386200, 384500, 386200, 386700, 386700, 386700, 384300, 384400, 384500, 384600, 384300, 384700, 384700, 384700, 385500, 384300, 384300] + Y = [6109500, 6103800, 6104700, 6105500, 6106700, 6107800, 6108600, 6109500, 6110500, 6111500, 6112400, 6113400, 6114000, 6114200, 6115100, 6115900, 6116700, 6118400, 6119200, 6120000, 6120800, 6121800, 6122400, 6122000, 6121700, 6121000, 6120000, 6119100, 6118100, 6117200, 6116200, 6115300, 6114300, 6113400, 6112400, 6111500, 6110700, 6117600, 6108900, 6108100, 6107400, 6106300, 6105200, 6104400, 6103600, 6103600, 6103500, 6103400, 6103400, 6104400, 6120400, 6119500, 6118400, 6117400, 6116500, 6115500, 6114600, 6113500, 6112500, 6111500, 6105400, 6104200, 6110400, 6109400, 6108400, 6121300, 6107500, 6106400, 6105300, 6104400, 6113300, 6112500, 6111600, 6110800, 6110100, 6109200, 6108400, 6107600, 6106500, 6106600, 6105000] + X = np.array(X) + Y = np.array(Y) + + option = 3 + UL = 11 + Inters_const = False + Cables = np.array([[500, 3, 100000], [800, 5, 150000], [1000, 10, 250000]]) + + T, feasible = capacitated_spanning_tree(X, Y, option, UL, Inters_const) + + print("The total length of the solution is {value:.2f} m".format(value=sum(T[:, 2]))) + print("Feasibility: {feasible1}".format(feasible1=feasible)) + + if feasible: + T_cables = cmst_cables(X, Y, T, Cables, plot=True) + print("The total cost of the system is {value:.2f} Euros".format(value=T_cables[:, -1].sum())) + elapsed = time.time() - t + print("The total time is {timep: .2f} s".format(timep=elapsed)) diff --git a/ed_win/collection_system.py b/ed_win/drivers/tsh/collection_system.py similarity index 85% rename from ed_win/collection_system.py rename to ed_win/drivers/tsh/collection_system.py index 801c332..a852c40 100644 --- a/ed_win/collection_system.py +++ b/ed_win/drivers/tsh/collection_system.py @@ -11,12 +11,15 @@ from ed_win.c_mst_cables import cmst_cables def collection_system(X=[], Y=[], option=3, Inters_const=True, max_it=20000, Cables=[], plot=False): UL = max(Cables[:, 1]) - T, feasible = capacitated_spanning_tree(X, Y, option, UL, Inters_const) - T_cables_cost = ((T[:, -1].sum()) / 1000) * Cables[-1, 2] - T_cables = np.concatenate((T, np.full((T.shape[0], 2), Cables.shape[1] - 1)), axis=1) - if feasible: - T_cables = cmst_cables(X, Y, T, Cables, plot) - T_cables_cost = T_cables[:, -1].sum() + feasible = False + while not feasible: + T, feasible = capacitated_spanning_tree(X, Y, option, UL, Inters_const, max_it) + T_cables_cost = ((T[:, -1].sum())) * Cables[-1, 2] + T_cables = np.concatenate((T, np.full((T.shape[0], 2), Cables.shape[1] - 1)), axis=1) + if feasible: + T_cables = cmst_cables(X, Y, T, Cables, plot) + T_cables_cost = T_cables[:, -1].sum() + feasible = True return T_cables, T_cables_cost diff --git a/ed_win/intersection_checker.py b/ed_win/drivers/tsh/intersection_checker.py similarity index 100% rename from ed_win/intersection_checker.py rename to ed_win/drivers/tsh/intersection_checker.py diff --git a/ed_win/two_lines_intersecting.py b/ed_win/drivers/tsh/two_lines_intersecting.py similarity index 100% rename from ed_win/two_lines_intersecting.py rename to ed_win/drivers/tsh/two_lines_intersecting.py diff --git a/ed_win/edwin_lib.py b/ed_win/edwin_lib.py new file mode 100644 index 0000000..498d459 --- /dev/null +++ b/ed_win/edwin_lib.py @@ -0,0 +1,243 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import functools +import math +import operator +from collections import defaultdict +from itertools import chain, product +from math import isclose +from typing import Tuple +import shapely as shp +import networkx as nx +import numpy as np +from scipy.sparse import coo_array +from scipy.sparse.csgraph import minimum_spanning_tree as scipy_mst +from scipy.spatial import Delaunay +from scipy.spatial.distance import cdist + + +def make_graph_metrics(G): + """ + The function is based on the functions developed in interarry module. + This function changes G in place! + Calculates for all nodes, for each root node: + - distance to root nodes + - angle wrt root node + + Any detour nodes in G are ignored. + """ + VertexC = G.graph['VertexC'] + M = G.graph['M'] + # N = G.number_of_nodes() - M + roots = range(-M, 0) + NodeC = VertexC[:-M] + RootC = VertexC[-M:] + + # calculate distance from all nodes to each of the roots + d2roots = cdist(VertexC[:-M], VertexC[-M:]) + + angles = np.empty_like(d2roots) + for n, nodeC in enumerate(NodeC): + nodeD = G.nodes[n] + # assign the node to the closest root + nodeD['root'] = -M + np.argmin(d2roots[n]) + x, y = (nodeC - RootC).T + angles[n] = np.arctan2(y, x) + # TODO: ¿is this below actually used anywhere? + # assign root nodes to themselves (for completeness?) + for root in roots: + G.nodes[root]['root'] = root + + G.graph['d2roots'] = d2roots + G.graph['d2rootsRank'] = np.argsort(np.argsort(d2roots, axis=0), axis=0) + G.graph['angles'] = angles + G.graph['anglesRank'] = np.argsort(np.argsort(angles, axis=0), axis=0) + G.graph['anglesYhp'] = angles >= 0. + G.graph['anglesXhp'] = abs(angles) < np.pi / 2 + + +def check_crossings(G, MARGIN=0.1): + """ + The function is based on the functions developed in interarry module. + Check for crossings between edges in a graph. + + Parameters: + G (networkx.Graph): The input graph. + MARGIN (float): The threshold for considering edges as crossing. + Defaults to 0.1. + + Returns: + bool: True if any crossings are detected, False otherwise. + """ + # Extract necessary information from the graph + VertexC = G.graph['VertexC'] + M = G.graph['M'] + N = G.number_of_nodes() - G.graph.get('D', 0) + D = G.graph.get('D') + + # Adjust the number of nodes if D is not None + if D is not None: + N -= D + fnT = G.graph['fnT'] + AllnodesC = np.vstack((VertexC[:N], VertexC[fnT[N:N + D]], VertexC[-M:])) + else: + fnT = np.arange(N + M) + AllnodesC = VertexC + + # Define roots + roots = range(-M, 0) + fnT[-M:] = roots + + # Check for crossings + for root in roots: + edges = list(nx.edge_bfs(G, source=root)) + for i, (u, v) in enumerate(edges): + for s, t in edges[(i + 1):]: + if is_crossing(*AllnodesC[[u, v, s, t]], touch_is_cross=True): + return True # Crossing detected + return False # No crossing detected + + +def is_crossing(u, v, w, y, touch_is_cross=True): + """ + The function is based on the functions developed in interarry module. + Check if the line segment (u, v) crosses (w, y). + Parameters: + u, v, w, y (float): Coordinates of the line segments. + touch_is_cross (bool): Whether touching is considered as crossing. + + Returns: + bool: True if there is a crossing, False otherwise. + """ + less = operator.lt if touch_is_cross else operator.le + + # Calculate differences + A = v - u + B = w - y + + # Bounding box check + for i in (0, 1): # X and Y + lo, hi = (v[i], u[i]) if A[i] < 0 else (u[i], v[i]) + if (B[i] > 0 and (hi < y[i] or w[i] < lo)) or (B[i] < 0 and (hi < w[i] or y[i] < lo)): + return False + + # Calculate denominator + f = B[0] * A[1] - B[1] * A[0] + if np.isclose(f, 0, atol=1e-3): + return False + + # Calculate numerators and check if crossing + for num, den in ((Px * Qy - Py * Qx, f) for (Px, Py), (Qx, Qy) in ((u - w, B), (A, u - w))): + if (f > 0 and (less(num, 0) or less(f, num))) or (f < 0 and (less(0, num) or less(num, f))): + return False + return True + + +def is_graph_connected(G): + """ + Check if the graph is connected (all nodes are connected). + + Parameters: + G (networkx.Graph): The input graph. + + Returns: + bool: True if the graph is connected, False otherwise. + """ + # Use Depth-First Search (DFS) to traverse the graph + visited = set() + + def dfs(node): + visited.add(node) + for neighbor in G.neighbors(node): + if neighbor not in visited: + dfs(neighbor) + + # Start DFS from an arbitrary node + arbitrary_node = next(iter(G.nodes), None) + if arbitrary_node is None: + # Graph is empty, so technically it's connected + return True + + dfs(arbitrary_node) + + # Check if all nodes are visited + return len(visited) == len(G.nodes) + + +def is_inside_boundary(boundary, coordinates): + """ + Check if all turbine coordinates are inside the defined boundary. + + Args: + - boundary (list): List of boundary coordinates [(x1, y1), (x2, y2), ..., (xn, yn)] forming a closed polygon. + - turbine_coordinates (list): List of turbine coordinates [(x1, y1), (x2, y2), ..., (xn, yn)]. + + Returns: + - bool: True if all turbine coordinates are inside the boundary, False otherwise. + """ + + # Function to check if a point (x, y) is inside a polygon defined by vertices + def is_inside_polygon(x, y, vertices): + """ + Check if a point (x, y) is inside a polygon defined by vertices. + + Args: + - x (float): x-coordinate of the point. + - y (float): y-coordinate of the point. + - vertices (list): List of vertex coordinates [(x1, y1), (x2, y2), ..., (xn, yn)] forming a closed polygon. + + Returns: + - bool: True if the point is inside the polygon, False otherwise. + """ + # Initialize variables + inside = False + j = len(vertices) - 1 + + # Loop through each edge of the polygon + for i in range(len(vertices)): + xi, yi = vertices[i] + xj, yj = vertices[j] + + # Check if the point is inside the edge + if (yi < y <= yj or yj < y <= yi) and (xi <= x or xj <= x): + if xi + (y - yi) / (yj - yi) * (xj - xi) < x: + inside = not inside + j = i + + return inside + + # Check if all turbine coordinates are inside the boundary + for coordinate in coordinates: + x, y = coordinate + if not is_inside_polygon(x, y, boundary): + return False + + return True + + +def graph_to_table(G): + """Convert graph to table format.""" + M = G.graph['M'] + Ne = G.number_of_edges() + + def edge_parser(edges): + for u, v, data in edges: + s = (u + M + 1) if u >= 0 else abs(u) + t = (v + M + 1) if v >= 0 else abs(v) + yield (s, t, data['length'], data.get('load', 0), data.get('cable', 0), data.get('cost', 0)) + + T = np.fromiter(edge_parser(G.edges(data=True)), + dtype=[('u', int), ('v', int), ('length', float), + ('load', int), ('cable', int), ('cost', float)], + count=Ne) + return T + + +def graph_to_yaml(G): + """Convert graph to YAML format.""" + graph_data = { + 'nodes': [{'id': node, **data} for node, data in G.nodes(data=True)], + 'edges': [{'source': u, 'target': v, **data} for u, v, data in G.edges(data=True)] + } + return yaml.dump(graph_data) diff --git a/ed_win/examples/__init__.py b/ed_win/examples/__init__.py new file mode 100644 index 0000000..f5e07a1 --- /dev/null +++ b/ed_win/examples/__init__.py @@ -0,0 +1,2 @@ +import numpy +npt = numpy.testing diff --git a/ed_win/examples/example_optimize_vs_evaluate.py b/ed_win/examples/example_optimize_vs_evaluate.py new file mode 100644 index 0000000..8b49d44 --- /dev/null +++ b/ed_win/examples/example_optimize_vs_evaluate.py @@ -0,0 +1,178 @@ +from ed_win.wind_farm_network import WindFarmNetwork, InterArrayDriver, GeneticAlgorithmDriver +import matplotlib.pyplot as plt +import numpy as np +import psutil + + +# Get memory information +memory_info = psutil.virtual_memory() + +# Print available memory +print(f"Total Memory: {memory_info.total / (1024 ** 3):.2f} GB") +print(f"Available Memory: {memory_info.available / (1024 ** 3):.2f} GB") + +# Get CPU count and usage +cpu_count = psutil.cpu_count(logical=False) +cpu_count_logical = psutil.cpu_count(logical=True) +cpu_percent = psutil.cpu_percent(interval=1) + +# Get CPU frequency +cpu_freq = psutil.cpu_freq() + +# Print the information +print(f"Physical CPUs: {cpu_count}\nLogical CPUs: {cpu_count_logical}\nCPU Usage: {cpu_percent}%") +print(f"CPU Frequency: {cpu_freq.current} MHz") + +# Data extracted from IEA37 Borssele load_data +cable_costs = [206, 287, 406] # [€/m] Costs per distance for each cable type +turbines_per_cable = [3, 5, 7] +cross_section = [500, 1000, 1500] + +cables = np.array([[500, 3, 206], [800, 5, 287], [1000, 7, 406]]) +# From manual input +# with mininum required inputs +# Turbine and substation positions from IEA37 Borssele Regular System +BoundaryC = np.array(list(zip([484178.55, 500129.9, 497318.1, + 503163.37, 501266.5, 488951.0], + [5732482.8, 5737534.4, 5731880.24, + 5729155.3, 5715990.05, 5727940.]))) +site_info = {'site_name': 'IEA-37 Regular', + 'handle': 'iea37reg', + 'boundary': BoundaryC + } + +# Turbine and substation positions from IEA37 Borssele Regular System +substations_pos = np.asarray([[497620.7], [5730622.0]]).T +turbines_pos = np.array([[500968.1461, 499748.6565, 501245.7744, 500026.2848, + 498527.8286, 497308.339, 501523.4027, 500303.9131, + 498805.4569, 497585.9673, 496087.5111, 494868.0215, + 501801.031, 500581.5414, 499083.0852, 497863.5956, + 496365.1394, 495145.6498, 493647.1936, 492427.704, + 502078.6593, 500859.1697, 499360.7135, 498141.2239, + 496642.7677, 495423.2781, 493924.8219, 492705.3323, + 491206.8762, 489987.3865, 502356.2876, 501136.798, + 499638.3418, 498418.8522, 496920.396, 495700.9064, + 494202.4502, 492982.9606, 491484.5045, 490265.0148, + 488766.5587, 487547.069, 502633.9159, 501414.4263, + 499915.9701, 498696.4805, 497198.0243, 495978.5347, + 494480.0786, 493260.5889, 491762.1328, 490542.6431, + 489044.187, 487824.6973, 486326.2412, 485106.7515, + 497475.6526, 496256.163, 494757.7069, 493538.2172, + 492039.7611, 490820.2714, 489321.8153, 488102.3256, + 486603.8695, 497753.2809, 496533.7913, 495035.3352, + 493815.8455, 492317.3894, 489599.4436, 498030.9093, + 496811.4196, 495312.9635], + [5716452.784, 5717635.848, 5718427.22, 5719610.283, + 5718809.394, 5719992.458, 5720401.656, 5721584.719, + 5720783.83, 5721966.894, 5721166.004, 5722349.068, + 5722376.092, 5723559.155, 5722758.266, 5723941.33, + 5723140.44, 5724323.504, 5723522.615, 5724705.678, + 5724350.528, 5725533.591, 5724732.702, 5725915.765, + 5725114.876, 5726297.94, 5725497.051, 5726680.114, + 5725879.225, 5727062.288, 5726324.963, 5727508.027, + 5726707.138, 5727890.201, 5727089.312, 5728272.376, + 5727471.486, 5728654.55, 5727853.661, 5729036.724, + 5728235.835, 5729418.899, 5728299.399, 5729482.463, + 5728681.574, 5729864.637, 5729063.748, 5730246.812, + 5729445.922, 5730628.986, 5729828.097, 5731011.16, + 5730210.271, 5731393.335, 5730592.445, 5731775.509, + 5731038.184, 5732221.248, 5731420.358, 5732603.422, + 5731802.533, 5732985.596, 5732184.707, 5733367.77, + 5732566.881, 5733012.62, 5734195.683, 5733394.794, + 5734577.858, 5733776.968, 5734159.143, 5734987.056, + 5736170.119, 5735369.23]]).T + +wfn = WindFarmNetwork(turbines_pos=turbines_pos, substations_pos=substations_pos, cables=cables, site_info=site_info) + + +# Run with initial inputs +H1 = wfn.optimize() +print(round(H1.size(weight='cost')), '€') +H1.plot() + +# update turbines coordinates +turbines_pos = np.array([[500968.1461, 499748.6565, 501245.7744, 500026.2848, + 498527.8286, 497308.339, 501523.4027, 500303.9131,], + [5716452.784, 5717635.848, 5718427.22, 5719610.283, + 5718809.394, 5719992.458, 5720401.656, 5721584.719,]]).T +H2 = wfn.optimize(turbines_pos=turbines_pos) +print(round(H2.size(weight='cost')), '€') +H2.plot() + +# update substation coordinates +substations_pos = np.asarray([[500000], [5735000]]).T +H3 = wfn.optimize(substations_pos=substations_pos) +print(round(H3.size(weight='cost')), '€') +H3.plot() + +# rerun optimize +H4 = wfn.optimize() +print(round(H4.size(weight='cost')), '€') +H4.plot() + +# update coordinates for evaluate +turbines_pos = np.array([[ + 486603.8695, 497753.2809, 496533.7913, 495035.3352, + 493815.8455, 492317.3894, 489599.4436, 498030.9093], + [ + 5732566.881, 5733012.62, 5734195.683, 5733394.794, + 5734577.858, 5733776.968, 5734159.143, 5734987.056,]]).T +H5 = wfn.evaluate(turbines_pos=turbines_pos) +print(round(H5.size(weight='cost')), '€') +H5.plot() + +substations_pos = np.asarray([[499620.7], [5711622.0]]).T +print('Evaluate:') +H6 = wfn.evaluate(substations_pos=substations_pos, update_selfG=True) +H6.plot() + +substations_pos = np.asarray([[497620.7], [5730622.0]]).T +H7 = wfn.evaluate(substations_pos=substations_pos, update_selfG=True) +print(round(H7.size(weight='cost')), '€') +H7.plot() +plt.show() + +# changing number of turbines +turbines_pos = np.array([[500968.1461, 499748.6565, 501245.7744, 500026.2848, + 498527.8286, 497308.339, 501523.4027, 500303.9131, + 498805.4569, 497585.9673, 496087.5111, 494868.0215, + 501801.031, 500581.5414, 499083.0852, 497863.5956, + 496365.1394, 495145.6498, 493647.1936, 492427.704, + 502078.6593, 500859.1697, 499360.7135, 498141.2239, + 496642.7677, 495423.2781, 493924.8219, 492705.3323, + 491206.8762, 489987.3865, 502356.2876, 501136.798, + 499638.3418, 498418.8522, 496920.396, 495700.9064, + 494202.4502, 492982.9606, 491484.5045, 490265.0148, + 488766.5587, 487547.069, 502633.9159, 501414.4263, + 499915.9701, 498696.4805, 497198.0243, 495978.5347, + 494480.0786, 493260.5889, 491762.1328, 490542.6431, + 489044.187, 487824.6973, 486326.2412, 485106.7515, + 497475.6526, 496256.163, 494757.7069, 493538.2172, + 492039.7611, 490820.2714, 489321.8153, 488102.3256, + 486603.8695, 497753.2809, 496533.7913, 495035.3352, + 493815.8455, 492317.3894, 489599.4436, 498030.9093, + 496811.4196, 495312.9635], + [5716452.784, 5717635.848, 5718427.22, 5719610.283, + 5718809.394, 5719992.458, 5720401.656, 5721584.719, + 5720783.83, 5721966.894, 5721166.004, 5722349.068, + 5722376.092, 5723559.155, 5722758.266, 5723941.33, + 5723140.44, 5724323.504, 5723522.615, 5724705.678, + 5724350.528, 5725533.591, 5724732.702, 5725915.765, + 5725114.876, 5726297.94, 5725497.051, 5726680.114, + 5725879.225, 5727062.288, 5726324.963, 5727508.027, + 5726707.138, 5727890.201, 5727089.312, 5728272.376, + 5727471.486, 5728654.55, 5727853.661, 5729036.724, + 5728235.835, 5729418.899, 5728299.399, 5729482.463, + 5728681.574, 5729864.637, 5729063.748, 5730246.812, + 5729445.922, 5730628.986, 5729828.097, 5731011.16, + 5730210.271, 5731393.335, 5730592.445, 5731775.509, + 5731038.184, 5732221.248, 5731420.358, 5732603.422, + 5731802.533, 5732985.596, 5732184.707, 5733367.77, + 5732566.881, 5733012.62, 5734195.683, 5733394.794, + 5734577.858, 5733776.968, 5734159.143, 5734987.056, + 5736170.119, 5735369.23]]).T + +H = wfn.evaluate(turbines_pos=turbines_pos) + + +# End of the code! diff --git a/ed_win/plotting.py b/ed_win/plotting.py new file mode 100644 index 0000000..f2141b6 --- /dev/null +++ b/ed_win/plotting.py @@ -0,0 +1,261 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +from collections import defaultdict +from collections.abc import Sequence +from typing import Optional + +import matplotlib.pyplot as plt +import networkx as nx +import numpy as np +from matplotlib import animation +from matplotlib.patches import Polygon +from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar + +from .plotting_scripts.geometric import make_graph_metrics, rotate +from .plotting_scripts.interarraylib import calcload + + +FONTSIZE_LABEL = 6 +FONTSIZE_LOAD = 8 +FONTSIZE_ROOT_LABEL = 6 +FONTSIZE_LEGEND_BOX = 7 +FONTSIZE_LEGEND_STRIP = 6 +NODESIZE = 18 +NODESIZE_LABELED = 70 +NODESIZE_LABELED_ROOT = 28 +NODESIZE_DETOUR = 80 +NODESIZE_LABELED_DETOUR = 150 + + +# Define the configurations +rcParams = { + 'figure.constrained_layout.use': True, + 'figure.facecolor': 'white', + 'savefig.facecolor': 'white', + 'savefig.transparent': True, + 'figure.dpi': 192, + 'figure.frameon': False, + 'figure.edgecolor': 'none', # Corrected value + 'savefig.bbox': 'tight' +} + +# Update Matplotlib rcParams with defined configurations +plt.rcParams.update(rcParams) + + +def gplot(G, ax=None, node_tag=None, edge_exemption=False, figlims=(5, 6), + landscape=True, infobox=True): + '''NetworkX graph plotting function. + `node_tag` in [None, 'load', 'label'] + (or other key in nodes's dict)''' + figsize = plt.rcParams['figure.figsize'] + dark = plt.rcParams['figure.facecolor'] != 'white' + + root_size = NODESIZE_LABELED_ROOT if node_tag is not None else NODESIZE + detour_size = NODESIZE_LABELED_DETOUR if node_tag is not None else NODESIZE_DETOUR + node_size = NODESIZE_LABELED if node_tag is not None else NODESIZE + + type2color = {} + type2style = dict( + detour='dashed', + scaffold='dotted', + extended='dashed', + delaunay='solid', + unspecified='solid', + ) + if dark: + scalebar = False + type2color.update( + detour='darkorange', + scaffold='gray', + delaunay='darkcyan', + extended='darkcyan', + unspecified='crimson', + ) + root_color = 'lawngreen' + node_edge = 'none' + detour_ring = 'orange' + polygon_edge = 'none' + polygon_face = '#111111' + else: + scalebar = True + type2color.update( + detour='royalblue', + scaffold='gray', + delaunay='black', + extended='black', + unspecified='firebrick', + ) + root_color = 'black' if node_tag is None else 'yellow' + node_edge = 'black' + detour_ring = 'deepskyblue' + polygon_edge = '#444444' + polygon_face = 'whitesmoke' + + landscape_angle = G.graph.get('landscape_angle') + if landscape and landscape_angle: + # landscape_angle is not None and not 0 + VertexC = rotate(G.graph['VertexC'], landscape_angle) + else: + VertexC = G.graph['VertexC'] + M = G.graph['M'] + N = G.number_of_nodes() - M + D = G.graph.get('D') + + # draw farm boundary + if 'boundary' in G.graph and G.graph['boundary'] is not None: + BoundaryC = (rotate(G.graph['boundary'], landscape_angle) + if landscape and landscape_angle else + G.graph['boundary']) + if ax is None and not dark: + limX, limY = figlims + r = limX / limY + XYrange = np.abs(np.amax(BoundaryC, axis=0) - + np.amin(BoundaryC, axis=0)) + d = XYrange[0] / XYrange[1] + if d < r: + figsize = (limY * d, limY) + else: + figsize = (limX, limX / d) + + area_polygon = Polygon(BoundaryC, zorder=0, linestyle='--', + facecolor=polygon_face, edgecolor=polygon_edge, + linewidth=0.3) + if ax is None: + fig, ax = plt.subplots(figsize=figsize) + ax.add_patch(area_polygon) + ax.update_datalim(area_polygon.get_xy()) + ax.autoscale() + elif ax is None: + fig, ax = plt.subplots(figsize=figsize) + ax.axis('off') + + ax.set_aspect('equal') + # setup + roots = range(-M, 0) + pos = dict(zip(range(N), VertexC[:N])) | dict(zip(roots, VertexC[-M:])) + if D is not None: + N -= D + fnT = G.graph.get('fnT') + detour = range(N, N + D) + pos |= dict(zip(detour, VertexC[fnT[detour]])) + RootL = {r: G.nodes[r]['label'] for r in roots[::-1]} + + colors = plt.cm.get_cmap('tab20', 20).colors + # default value for subtree (i.e. color for unconnected nodes) + # is the last color of the tab20 colormap (i.e. 19) + subtrees = G.nodes(data='subtree', default=19) + node_colors = [colors[subtrees[n] % len(colors)] for n in range(N)] + + cables_capacity = G.graph['cables']['capacity'] + + # draw edges + edges = G.edges(data='cable') + cable_types = [edge[-1] for edge in edges] + cable_types_unique = list(set(cable_types)) + + # draw edges without type + for cable_type in cable_types_unique: + nx.draw_networkx_edges(G, pos, ax=ax, edge_color=type2color['unspecified'], + style=type2style['unspecified'], label=f"{int(G.graph['cables']['area'][cable_type])} mm²", + edgelist=[(u, v) for u, v, data in G.edges(data=True) + if data['cable'] == cable_type and data.get('type', None) is None], width=(cable_type + 0.5) / 2) + + # draw edges with type + for edge_type in type2style: + edge_data = [(u, v, data['cable']) for u, v, data in G.edges(data=True) if data.get('type', None) == edge_type] + nx.draw_networkx_edges(G, pos, ax=ax, edge_color=type2color[edge_type], + style=type2style[edge_type], label=edge_type, + edgelist=[(u, v) for u, v, data in edge_data], + width=[(cable_type + 0.5) / 2 for _, _, cable_type in edge_data]) + + # draw nodes + if D is not None: + # draw circunferences around nodes that have Detour clones + nx.draw_networkx_nodes(G, pos, ax=ax, nodelist=detour, alpha=0.4, + edgecolors=detour_ring, node_color='none', + node_size=detour_size, + label='corner') + nx.draw_networkx_nodes(G, pos, ax=ax, nodelist=roots, linewidths=0.2, + node_color=root_color, edgecolors=node_edge, + node_size=root_size, node_shape='s', + label='OSS') + nx.draw_networkx_nodes(G, pos, nodelist=range(N), edgecolors=node_edge, + ax=ax, node_color=node_colors, node_size=node_size, + linewidths=0.2, label='WTG') + + # draw labels + font_size = dict(load=FONTSIZE_LOAD, + label=FONTSIZE_LABEL, + tag=FONTSIZE_ROOT_LABEL) + if node_tag is not None: + if node_tag == 'load' and 'has_loads' not in G.graph: + node_tag = 'label' + labels = nx.get_node_attributes(G, node_tag) + for root in roots: + if root in labels: + labels.pop(root) + if D is not None: + for det in detour: + if det in labels: + labels.pop(det) + nx.draw_networkx_labels(G, pos, ax=ax, font_size=font_size[node_tag], + labels=labels) + # root nodes' labels + if node_tag is not None: + nx.draw_networkx_labels(G, pos, ax=ax, font_size=FONTSIZE_ROOT_LABEL, + labels=RootL) + + if scalebar: + bar = AnchoredSizeBar(ax.transData, 1000, '1 km', 'lower right', + frameon=False) + ax.add_artist(bar) + + if infobox: + info = [] + if 'capacity' in G.graph: + info = [f'$n_{{\\mathrm{{max}}}}$ = {G.graph["capacity"]}, ' + f'$N$ = {N}'] + feeder_info = [f'$\\phi_{{{rootL}}}$ = {len(G[r])}' + for r, rootL in RootL.items()] + if 'overfed' in G.graph: + feeder_info = [fi + f' ({100*(overfed - 1):+.0f}%)' + for fi, overfed in + zip(feeder_info, G.graph['overfed'][::-1])] + info.extend(feeder_info) + # legend.append(', '.join(feeder_info)) + Gʹ = nx.subgraph_view(G, + filter_edge=lambda u, v: 'length' in G[u][v]) + length = Gʹ.size(weight="length") + intdigits = int(np.floor(np.log10(length))) + 1 + info.append(f'Σl = {round(length, max(0, 5 - intdigits))} m') + # assert Gʹ.number_of_edges() == G.number_of_nodes() - 1, \ + # f'{Gʹ.number_of_edges()} != {G.number_of_nodes()}' + # for field, sym in (('weight', 'w'), ('length', 'l')): + # for field, sym in (('length', ''),): + # weight = field if all([(field in data) + # for _, _, data in G.edges.data()]) else None + # legend.append('Σ{} = {:.0f}'.format(sym, G.size(weight=weight)) + + # ' m' if field == 'length' else '') + if ('has_costs' in G.graph): + info.append('{:.0f} €'.format(G.size(weight='cost'))) + if 'capacity' in G.graph: + infobox = ax.legend([], fontsize=FONTSIZE_LEGEND_BOX, + title='\n'.join(info), labelspacing=0) + # loc='upper right' + # bbox_to_anchor=(-0.04, 0.80, 1.08, 0) + # bbox_to_anchor=(-0.04, 1.03, 1.08, 0) + plt.setp(infobox.get_title(), multialignment='center') + # ax.legend(title='\n'.join(legend)) + # legend1 = pyplot.legend(plot_lines[0], ["algo1", "algo2", "algo3"], loc=1) + # pyplot.legend([l[0] for l in plot_lines], parameters, loc=4) + # ax.add_artist(legstrip) + if not dark: + ax.legend(ncol=8, fontsize=FONTSIZE_LEGEND_STRIP, loc='lower center', + frameon=False, bbox_to_anchor=(0.5, -0.07), + columnspacing=1, handletextpad=0.3) + if 'capacity' in G.graph and infobox: + ax.add_artist(infobox) + + return ax diff --git a/ed_win/plotting_scripts/ClassicEsauWilliams.py b/ed_win/plotting_scripts/ClassicEsauWilliams.py new file mode 100644 index 0000000..afc1d3f --- /dev/null +++ b/ed_win/plotting_scripts/ClassicEsauWilliams.py @@ -0,0 +1,387 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import operator +import time + +import numpy as np + +from .geometric import (angle, apply_edge_exemptions, complete_graph, delaunay, + is_same_side) +from .interarraylib import new_graph_like +from .utils import NodeTagger +from .priorityqueue import PriorityQueue + + +F = NodeTagger() + + +def ClassicEW(G_base, capacity=8, delaunay_based=False, maxiter=10000, + debug=False, weightfun=None, weight_attr='length'): + '''Classic Esau-Williams heuristic for C-MST + inputs: + G_base: networkx.Graph + c: capacity + returns G_cmst: networkx.Graph''' + + start_time = time.perf_counter() + # grab relevant options to store in the graph later + options = dict(delaunay_based=delaunay_based) + + M = G_base.graph['M'] + N = G_base.number_of_nodes() - M + # roots = range(N, N + M) + roots = range(-M, 0) + VertexC = G_base.graph['VertexC'] + d2roots = G_base.graph['d2roots'] + d2rootsRank = G_base.graph['d2rootsRank'] + anglesRank = G_base.graph['anglesRank'] + anglesYhp = G_base.graph['anglesYhp'] + anglesXhp = G_base.graph['anglesXhp'] + + # BEGIN: prepare auxiliary graph with all allowed edges and metrics + if delaunay_based: + G_edges = delaunay(G_base) + # apply weightfun on all delaunay edges + if weightfun is not None: + apply_edge_exemptions(G_edges) + # TODO: decide whether to keep this 'else' (to get edge arcs) + # else: + # apply_edge_exemptions(G_edges) + else: + G_edges = complete_graph(G_base) + if weightfun is not None: + options['weightfun'] = weightfun.__name__ + options['weight_attr'] = weight_attr + for u, v, data in G_edges.edges(data=True): + data[weight_attr] = weightfun(data) + # removing root nodes from G_edges to speedup find_option4gate + # this may be done because G already starts with gates + G_edges.remove_nodes_from(roots) + # END: prepare auxiliary graph with all allowed edges and metrics + + # BEGIN: create initial star graph + star_edges = [] + for n in range(N): + root = G_base.nodes[n]['root'] + star_edges.append((root, n, {'length': d2roots[n, root]})) + G = new_graph_like(G_base, star_edges) + # END: create initial star graph + + # BEGIN: helper data structures + + # mappings from nodes + # <subtrees>: maps nodes to the set of nodes in their subtree + subtrees = np.array([{n} for n in range(N)]) + # <Gate>: maps nodes to their gates + Gate = np.array([n for n in range(N)]) + + # mappings from components (identified by their gates) + # <ComponIn>: maps component to set of components queued to merge in + ComponIn = np.array([set() for _ in range(N)]) + ComponLoLim = np.arange(N) # most CW node + ComponHiLim = np.arange(N) # most CCW node + + # mappings from roots + # <Final_G>: set of gates of finished components (one set per root) + Final_G = np.array([set() for _ in range(M)]) + + # other structures + # <pq>: queue prioritized by lowest tradeoff length + pq = PriorityQueue() + # find_option4gate() + # <gates2upd8>: deque for components that need to go through + gates2upd8 = set() + # <i>: iteration counter + i = 0 + # END: helper data structures + + def is_rank_within(rank, lowRank, highRank, anglesWrap, + touch_is_cross=False): + less = operator.le if touch_is_cross else operator.lt + if anglesWrap: + return less(rank, lowRank) or less(highRank, rank) + else: + return less(lowRank, rank) and less(rank, highRank) + + def is_crossing_gate(root, gate, u, v, touch_is_cross=False): + '''choices for `less`: + -> operator.lt: touching is not crossing + -> operator.le: touching is crossing''' + gaterank = anglesRank[gate, root] + uR, vR = anglesRank[u, root], anglesRank[v, root] + highRank, lowRank = (uR, vR) if uR >= vR else (vR, uR) + Xhp = anglesXhp[[u, v], root] + uYhp, vYhp = anglesYhp[[u, v], root] + if is_rank_within(gaterank, lowRank, highRank, + not any(Xhp) and uYhp != vYhp, touch_is_cross): + if not is_same_side(*VertexC[[u, v, root, gate]]): + # crossing gate + debug and print(f'<crossing> discarding ' + f'«{F[u]}–{F[v]}»: would cross' + f'gate <{F[gate]}>') + return True + return False + + def make_gate_final(root, g2keep): + Final_G[root].add(g2keep) + log.append((i, 'finalG', (g2keep, root))) + debug and print(f'<final> gate ' + f'[{F[g2keep]}] added') + + def component_merging_choices(gate, forbidden=None): + # gather all the edges leaving the subtree of gate + if forbidden is None: + forbidden = set() + forbidden.add(gate) + d2root = d2roots[gate, G_edges.nodes[gate]['root']] + capacity_left = capacity - len(subtrees[gate]) + weighted_edges = [] + edges2discard = [] + for u in subtrees[gate]: + for v in G_edges[u]: + if (Gate[v] in forbidden or + len(subtrees[v]) > capacity_left): + # useless edges + edges2discard.append((u, v)) + else: + W = G_edges[u][v][weight_attr] + # if W <= d2root: # TODO: what if I use <= instead of <? + if W < d2root: + # useful edges + tiebreaker = d2rootsRank[v, G_edges[u][v]['root']] + weighted_edges.append((W, tiebreaker, u, v)) + return weighted_edges, edges2discard + + def sort_union_choices(weighted_edges): + # this function could be outside esauwilliams() + unordchoices = np.array( + weighted_edges, + dtype=[('weight', np.float64), + ('vd2rootR', np.int_), + ('u', np.int_), + ('v', np.int_)]) + # result = np.argsort(unordchoices, order=['weight']) + # unordchoices = unordchoices[result] + + # DEVIATION FROM Esau-Williams + # rounding of weight to make ties more likely + # tie-breaking by proximity of 'v' node to root + # purpose is to favor radial alignment of components + tempchoices = unordchoices.copy() + # tempchoices['weight'] /= tempchoices['weight'].min() + # tempchoices['weight'] = (20*tempchoices['weight']).round() # 5% + + result = np.argsort(tempchoices, order=['weight', 'vd2rootR']) + choices = unordchoices[result] + return choices + + def find_option4gate(gate): + debug and print(f'<find_option4gate> starting... gate = ' + f'<{F[gate]}>') + # () get component expansion edges with weight + weighted_edges, edges2discard = component_merging_choices(gate) + # discard useless edges + G_edges.remove_edges_from(edges2discard) + # () sort choices + choices = sort_union_choices(weighted_edges) if weighted_edges else [] + # () check gate crossings + # choice = first_non_crossing(choices, gate) + if len(choices) > 0: + weight, _, u, v = choices[0] + choice = (weight, u, v) + else: + choice = False + if choice: + # merging is better than gate, submit entry to pq + weight, u, v = choice + # tradeoff calculation + tradeoff = weight - d2roots[gate, G_edges.nodes[gate]['root']] + pq.add(tradeoff, gate, (u, v)) + ComponIn[Gate[v]].add(gate) + debug and print(f'<pushed> g2drop <{F[gate]}>, ' + f'«{F[u]}–{F[v]}», tradeoff = {tradeoff:.1e}') + else: + # no viable edge is better than gate for this node + # this becomes a final gate + if i: # run only if not at i = 0 + # definitive gates at iteration 0 do not cross any other edges + # they are not included in Final_G because the algorithm + # considers the gates extending to infinity (not really) + root = G_edges.nodes[gate]['root'] + make_gate_final(root, gate) + # check_heap4crossings(root, gate) + debug and print('<cancelling>', F[gate]) + if gate in pq.tags: + # i=0 gates and check_heap4crossings reverse_entry + # may leave accepting gates out of pq + pq.cancel(gate) + + def ban_queued_edge(g2drop, u, v): + if (u, v) in G_edges.edges: + G_edges.remove_edge(u, v) + else: + debug and print('<<<< UNLIKELY <ban_queued_edge()> ' + f'({F[u]}, {F[v]}) not in G_edges.edges >>>>') + g2keep = Gate[v] + # TODO: think about why a discard was needed + ComponIn[g2keep].discard(g2drop) + # gates2upd8.appendleft(g2drop) + gates2upd8.add(g2drop) + # find_option4gate(g2drop) + + # BEGIN: block to be simplified + is_reverse = False + componin = g2keep in ComponIn[g2drop] + reverse_entry = pq.tags.get(g2keep) + if reverse_entry is not None: + _, _, _, (s, t) = reverse_entry + if (t, s) == (u, v): + # TODO: think about why a discard was needed + ComponIn[g2drop].discard(g2keep) + # this is assymetric on purpose (i.e. not calling + # pq.cancel(g2drop), because find_option4gate will do) + pq.cancel(g2keep) + find_option4gate(g2keep) + is_reverse = True + + # if this if is not visited, replace the above with ComponIn check + # this means that if g2keep is to also merge with g2drop, then the + # edge of the merging must be (v, u) + if componin != is_reverse: + print(f'«{F[u]}–{F[v]}», ' + f'g2drop <{F[g2drop]}>, g2keep <{F[g2keep]}> ' + f'componin: {componin}, is_reverse: {is_reverse}') + + # END: block to be simplified + + # TODO: check if this function is necessary (not used) + def abort_edge_addition(g2drop, u, v): + if (u, v) in G_edges.edges: + G_edges.remove_edge(u, v) + else: + print('<<<< UNLIKELY <abort_edge_addition()> ' + f'({F[u]}, {F[v]}) not in G_edges.edges >>>>') + ComponIn[Gate[v]].remove(g2drop) + find_option4gate(g2drop) + + # initialize pq + for n in range(N): + find_option4gate(n) + + log = [] + G.graph['log'] = log + loop = True + # BEGIN: main loop + while loop: + i += 1 + if i > maxiter: + print(f'ERROR: maxiter reached ({i})') + break + debug and print(f'[{i}]') + # debug and print(f'[{i}] bj–bm root: {G_edges.edges[(F.bj, F.bm)]["root"]}') + if gates2upd8: + debug and print('gates2upd8:', ', '.join(F[gate] for gate in + gates2upd8)) + while gates2upd8: + # find_option4gate(gates2upd8.popleft()) + find_option4gate(gates2upd8.pop()) + if not pq: + # finished + break + g2drop, (u, v) = pq.top() + debug and print(f'<popped> «{F[u]}–{F[v]}»,' + f' g2drop: <{F[g2drop]}>') + + g2keep = Gate[v] + root = G_edges.nodes[g2keep]['root'] + + capacity_left = capacity - len(subtrees[u]) - len(subtrees[v]) + + # assess the union's angle span + keepHi = ComponHiLim[g2keep] + keepLo = ComponLoLim[g2keep] + dropHi = ComponHiLim[g2drop] + dropLo = ComponLoLim[g2drop] + newHi = dropHi if angle(*VertexC[[keepHi, root, dropHi]]) > 0 else keepHi + newLo = dropLo if angle(*VertexC[[dropLo, root, keepLo]]) > 0 else keepLo + debug and print(f'<angle_span> //{F[newLo]} : ' + f'{F[newHi]}//') + + # edge addition starts here + subtree = subtrees[v] + subtree |= subtrees[u] + G.remove_edge(G_edges.nodes[u]['root'], g2drop) + log.append((i, 'remE', (G_edges.nodes[u]['root'], g2drop))) + + g2keep_entry = pq.tags.get(g2keep) + if g2keep_entry is not None: + _, _, _, (_, t) = g2keep_entry + # print('node', F[t], 'gate', F[Gate[t]]) + ComponIn[Gate[t]].remove(g2keep) + # TODO: think about why a discard was needed + ComponIn[g2keep].discard(g2drop) + + # update the component's angle span + ComponHiLim[g2keep] = newHi + ComponLoLim[g2keep] = newLo + + # assign root, gate and subtree to the newly added nodes + for n in subtrees[u]: + G_edges.nodes[n]['root'] = root + Gate[n] = g2keep + subtrees[n] = subtree + debug and print(f'<add edge> «{F[u]}-{F[v]}» gate ' + f'<{F[g2keep]}>, ' + f'heap top: <{F[pq[0][-2]]}>, ' + f'«{chr(8211).join([F[x] for x in pq[0][-1]])}»' + f' {pq[0][0]:.1e}' if pq else 'heap EMPTY') + G.add_edge(u, v, **G_edges.edges[u, v]) + log.append((i, 'addE', (u, v))) + # remove from consideration edges internal to subtrees + G_edges.remove_edge(u, v) + + # finished adding the edge, now check the consequences + if capacity_left > 0: + for gate in list(ComponIn[g2keep]): + if len(subtrees[gate]) > capacity_left: + ComponIn[g2keep].discard(gate) + gates2upd8.add(gate) + for gate in ComponIn[g2drop] - ComponIn[g2keep]: + if len(subtrees[gate]) > capacity_left: + gates2upd8.add(gate) + else: + ComponIn[g2keep].add(gate) + gates2upd8.add(g2keep) + else: + # max capacity reached: subtree full + if g2keep in pq.tags: # if required because of i=0 gates + pq.cancel(g2keep) + make_gate_final(root, g2keep) + # don't consider connecting to this full subtree nodes anymore + G_edges.remove_nodes_from(subtree) + for gate in ComponIn[g2drop] | ComponIn[g2keep]: + gates2upd8.add(gate) + # END: main loop + + if debug: + not_marked = [] + for root in roots: + for gate in G[root]: + if gate not in Final_G[root]: + not_marked.append(gate) + not_marked and print('@@@@ WARNING: gates ' + f'<{", ".join([F[gate] for gate in not_marked])}' + '> were not marked as final @@@@') + + # algorithm finished, store some info in the graph object + G.graph['iterations'] = i + G.graph['capacity'] = capacity + G.graph['overfed'] = [len(G[root]) / np.ceil(N / capacity) * M + for root in roots] + G.graph['edges_created_by'] = 'ClassicEW' + G.graph['edges_fun'] = ClassicEW + G.graph['creation_options'] = options + G.graph['runtime_unit'] = 's' + G.graph['runtime'] = time.perf_counter() - start_time + return G diff --git a/ed_win/plotting_scripts/CrossingPreventingEW.py b/ed_win/plotting_scripts/CrossingPreventingEW.py new file mode 100644 index 0000000..d39f025 --- /dev/null +++ b/ed_win/plotting_scripts/CrossingPreventingEW.py @@ -0,0 +1,594 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import operator +import time + +import numpy as np + +from .geometric import (angle, apply_edge_exemptions, complete_graph, + delaunay, edge_crossings, is_crossing, + is_same_side) +from .interarraylib import new_graph_like +from .utils import NodeTagger +from .priorityqueue import PriorityQueue + + +F = NodeTagger() + + +def CPEW(G_base, capacity=8, delaunay_based=True, maxiter=10000, + debug=False, weightfun=None, weight_attr='length'): + '''Crossing Preventing Esau-Williams heuristic for C-MST + inputs: + G_base: networkx.Graph + c: capacity + returns G_cmst: networkx.Graph''' + + start_time = time.perf_counter() + # grab relevant options to store in the graph later + options = dict(delaunay_based=delaunay_based) + + M = G_base.graph['M'] + N = G_base.number_of_nodes() - M + # roots = range(N, N + M) + roots = range(-M, 0) + VertexC = G_base.graph['VertexC'] + d2roots = G_base.graph['d2roots'] + d2rootsRank = G_base.graph['d2rootsRank'] + anglesRank = G_base.graph['anglesRank'] + anglesYhp = G_base.graph['anglesYhp'] + anglesXhp = G_base.graph['anglesXhp'] + + # BEGIN: prepare auxiliary graph with all allowed edges and metrics + if delaunay_based: + A = delaunay(G_base, bind2root=True) + P = A.graph['planar'] + diagonals = A.graph['diagonals'] + # A = delaunay_deprecated(G_base) + # triangles = A.graph['triangles'] + # triangles_exp = A.graph['triangles_exp'] + # apply weightfun on all delaunay edges + if weightfun is not None: + # TODO: fix `apply_edge_exemptions()` for the + # `delaunay()` without triangles + apply_edge_exemptions(A) + # TODO: decide whether to keep this 'else' (to get edge arcs) + # else: + # apply_edge_exemptions(A) + else: + A = complete_graph(G_base) + if weightfun is not None: + options['weightfun'] = weightfun.__name__ + options['weight_attr'] = weight_attr + for u, v, data in A.edges(data=True): + data[weight_attr] = weightfun(data) + # removing root nodes from A to speedup find_option4gate + # this may be done because G already starts with gates + A.remove_nodes_from(roots) + # END: prepare auxiliary graph with all allowed edges and metrics + + # BEGIN: create initial star graph + star_edges = [] + for n in range(N): + root = G_base.nodes[n]['root'] + star_edges.append((root, n, {'length': d2roots[n, root]})) + G = new_graph_like(G_base, star_edges) + # END: create initial star graph + + # BEGIN: helper data structures + + # mappings from nodes + # <subtrees>: maps nodes to the set of nodes in their subtree + subtrees = np.array([{n} for n in range(N)]) + # <Gate>: maps nodes to their gates + Gate = np.array([n for n in range(N)]) + + # mappings from components (identified by their gates) + # <ComponIn>: maps component to set of components queued to merge in + ComponIn = np.array([set() for _ in range(N)]) + ComponLoLim = np.arange(N) # most CW node + ComponHiLim = np.arange(N) # most CCW node + + # mappings from roots + # <Final_G>: set of gates of finished components (one set per root) + Final_G = np.array([set() for _ in range(M)]) + + # other structures + # <pq>: queue prioritized by lowest tradeoff length + pq = PriorityQueue() + # find_option4gate() + # <gates2upd8>: deque for components that need to go through + # gates2upd8 = deque() + gates2upd8 = set() + # <edges2ban>: deque for edges that should not be considered anymore + # edges2ban = deque() + # TODO: this is not being used, decide what to do about it + edges2ban = set() + # TODO: edges{N,C,V} could be used to vectorize the edge crossing detection + # <edgesN>: array of nodes of the edges of G (N×2) + # <edgesC>: array of node coordinates for edges of G (N×2×2) + # <edgesV>: array of vectors of the edges of G (N×2) + # <i>: iteration counter + i = 0 + # <prevented_crossing>: counter for edges discarded due to crossings + prevented_crossings = 0 + # END: helper data structures + + def is_rank_within(rank, lowRank, highRank, anglesWrap, + touch_is_cross=False): + less = operator.le if touch_is_cross else operator.lt + if anglesWrap: + return less(rank, lowRank) or less(highRank, rank) + else: + return less(lowRank, rank) and less(rank, highRank) + + def is_crossing_gate(root, gate, u, v, touch_is_cross=False): + '''choices for `less`: + -> operator.lt: touching is not crossing + -> operator.le: touching is crossing''' + gaterank = anglesRank[gate, root] + uR, vR = anglesRank[u, root], anglesRank[v, root] + highRank, lowRank = (uR, vR) if uR >= vR else (vR, uR) + Xhp = anglesXhp[[u, v], root] + uYhp, vYhp = anglesYhp[[u, v], root] + if is_rank_within(gaterank, lowRank, highRank, + not any(Xhp) and uYhp != vYhp, touch_is_cross): + if not is_same_side(*VertexC[[u, v, root, gate]]): + # crossing gate + debug and print(f'<crossing> discarding ' + f'«{F[u]}–{F[v]}»: would cross' + f'gate <{F[gate]}>') + return True + return False + + def make_gate_final(root, g2keep): + Final_G[root].add(g2keep) + log.append((i, 'finalG', (g2keep, root))) + debug and print(f'<final> gate ' + f'[{F[g2keep]}] added') + + def component_merging_choices(gate, forbidden=None): + # gather all the edges leaving the subtree of gate + if forbidden is None: + forbidden = set() + forbidden.add(gate) + d2root = d2roots[gate, A.nodes[gate]['root']] + capacity_left = capacity - len(subtrees[gate]) + weighted_edges = [] + edges2discard = [] + for u in subtrees[gate]: + for v in A[u]: + if (Gate[v] in forbidden or + len(subtrees[v]) > capacity_left): + # useless edges + edges2discard.append((u, v)) + else: + W = A[u][v][weight_attr] + # if W <= d2root: # TODO: what if I use <= instead of <? + if W < d2root: + # useful edges + tiebreaker = d2rootsRank[v, A[u][v]['root']] + weighted_edges.append((W, tiebreaker, u, v)) + return weighted_edges, edges2discard + + def sort_union_choices(weighted_edges): + # this function could be outside esauwilliams() + unordchoices = np.array( + weighted_edges, + dtype=[('weight', np.float64), + ('vd2rootR', np.int_), + ('u', np.int_), + ('v', np.int_)]) + # result = np.argsort(unordchoices, order=['weight']) + # unordchoices = unordchoices[result] + + # DEVIATION FROM Esau-Williams + # rounding of weight to make ties more likely + # tie-breaking by proximity of 'v' node to root + # purpose is to favor radial alignment of components + tempchoices = unordchoices.copy() + tempchoices['weight'] /= tempchoices['weight'].min() + tempchoices['weight'] = (20 * tempchoices['weight']).round() # 5% + + result = np.argsort(tempchoices, order=['weight', 'vd2rootR']) + choices = unordchoices[result] + return choices + + def first_non_crossing(choices, gate): + '''go through choices and return the first that does not cross a final + gate''' + # TODO: remove gate from the parameters + nonlocal prevented_crossings + found = False + # BEGIN: for loop that picks an edge + for weight, tiebreaker, u, v in choices: + found = True + root = A[u][v]['root'] + + # check if a gate is crossing the edge (u, v) + # TODO: this only looks at the gates connecting to the edges' + # closest root , is it relevant to look at all roots? + # PendingG = set() + + for finalG in Final_G[root]: + # TODO: test a gate exactly overlapping with a node + # Elaborating: angleRank will take care of this corner case. + # the gate will fall within one of the edges around the node + if is_crossing_gate(root, finalG, u, v): + # crossing gate, discard edge + prevented_crossings += 1 + # TODO: call ban_queued_edge (problem: these edges are not + # queued) + if (u, v) in A.edges: + A.remove_edge(u, v) + else: + debug and print('<<<<<< UNLIKELY.A first_non_crossing' + f'(): ({F[u]}, {F[v]}) not in ' + 'A.edges >>>>') + if Gate[v] in ComponIn[gate]: + # this means the target component was in line to + # connect to the current component + debug and print('<<<<<< UNLIKELY.B first_non_crossing' + f'(): Gate[{F[v]}] in ComponIn[' + f'{F[gate]}] >>>>>>>') + _, _, _, (s, t) = pq.tags.get(Gate[v]) + if t == u: + ComponIn[gate].remove(Gate[v]) + # PendingG.add(Gate[v]) + # gates2upd8.append(Gate[v]) + gates2upd8.add(Gate[v]) + + found = False + break + # for pending in PendingG: + # print(f'<pending> processing ' + # f'pending [{F[pending]}]') + # find_option4gate(pending) + if found: + break + # END: for loop that picks an edge + return (weight, u, v) if found else () + + def find_option4gate(gate): + debug and print(f'<find_option4gate> starting... gate = ' + f'<{F[gate]}>') + if edges2ban: + debug and print(f'<<<<<<<edges2ban>>>>>>>>>>> _{len(edges2ban)}_') + while edges2ban: + # edge2ban = edges2ban.popleft() + edge2ban = edges2ban.pop() + ban_queued_edge(*edge2ban) + # () get component expansion edges with weight + weighted_edges, edges2discard = component_merging_choices(gate) + # discard useless edges + A.remove_edges_from(edges2discard) + # () sort choices + choices = sort_union_choices(weighted_edges) if weighted_edges else [] + # () check gate crossings + choice = first_non_crossing(choices, gate) + if choice: + # merging is better than gate, submit entry to pq + weight, u, v = choice + # tradeoff calculation + tradeoff = weight - d2roots[gate, A.nodes[gate]['root']] + pq.add(tradeoff, gate, (u, v)) + ComponIn[Gate[v]].add(gate) + debug and print(f'<pushed> g2drop <{F[gate]}>, ' + f'«{F[u]}–{F[v]}», tradeoff = {tradeoff:.1e}') + else: + # no viable edge is better than gate for this node + # this becomes a final gate + if i: # run only if not at i = 0 + # definitive gates at iteration 0 do not cross any other edges + # they are not included in Final_G because the algorithm + # considers the gates extending to infinity (not really) + root = A.nodes[gate]['root'] + make_gate_final(root, gate) + check_heap4crossings(root, gate) + debug and print('<cancelling>', F[gate]) + if gate in pq.tags: + # i=0 gates and check_heap4crossings reverse_entry + # may leave accepting gates out of pq + pq.cancel(gate) + + def check_heap4crossings(root, finalG): + '''search the heap for edges that cross the gate 'finalG'. + calls find_option4gate for each of the subtrees involved''' + for tradeoff, _, g2drop, uv in pq: + # if uv is None or uv not in A.edges: + if uv is None: + continue + u, v = uv + if is_crossing_gate(root, finalG, u, v): + nonlocal prevented_crossings + # crossing gate, discard edge + prevented_crossings += 1 + ban_queued_edge(g2drop, u, v) + + def ban_queued_edge(g2drop, u, v): + if (u, v) in A.edges: + A.remove_edge(u, v) + else: + debug and print('<<<< UNLIKELY <ban_queued_edge()> ' + f'({F[u]}, {F[v]}) not in A.edges >>>>') + g2keep = Gate[v] + # TODO: think about why a discard was needed + ComponIn[g2keep].discard(g2drop) + # gates2upd8.appendleft(g2drop) + gates2upd8.add(g2drop) + # find_option4gate(g2drop) + + # BEGIN: block to be simplified + is_reverse = False + componin = g2keep in ComponIn[g2drop] + reverse_entry = pq.tags.get(g2keep) + if reverse_entry is not None: + _, _, _, (s, t) = reverse_entry + if (t, s) == (u, v): + # TODO: think about why a discard was needed + ComponIn[g2drop].discard(g2keep) + # this is assymetric on purpose (i.e. not calling + # pq.cancel(g2drop), because find_option4gate will do) + pq.cancel(g2keep) + find_option4gate(g2keep) + is_reverse = True + + # if this if is not visited, replace the above with ComponIn check + # this means that if g2keep is to also merge with g2drop, then the + # edge of the merging must be (v, u) + if componin != is_reverse: + print(f'«{F[u]}–{F[v]}», ' + f'g2drop <{F[g2drop]}>, g2keep <{F[g2keep]}> ' + f'componin: {componin}, is_reverse: {is_reverse}') + + # END: block to be simplified + + # TODO: check if this function is necessary (not used) + def abort_edge_addition(g2drop, u, v): + if (u, v) in A.edges: + A.remove_edge(u, v) + else: + print('<<<< UNLIKELY <abort_edge_addition()> ' + f'({F[u]}, {F[v]}) not in A.edges >>>>') + ComponIn[Gate[v]].remove(g2drop) + find_option4gate(g2drop) + + # initialize pq + for n in range(N): + find_option4gate(n) + + log = [] + G.graph['log'] = log + loop = True + # BEGIN: main loop + while loop: + i += 1 + if i > maxiter: + print(f'ERROR: maxiter reached ({i})') + break + debug and print(f'[{i}]') + # debug and print(f'[{i}] bj–bm root: {A.edges[(F.bj, F.bm)]["root"]}') + if gates2upd8: + debug and print('gates2upd8:', ', '.join(F[gate] for gate in + gates2upd8)) + while gates2upd8: + # find_option4gate(gates2upd8.popleft()) + find_option4gate(gates2upd8.pop()) + if not pq: + # finished + break + g2drop, (u, v) = pq.top() + debug and print(f'<popped> «{F[u]}–{F[v]}»,' + f' g2drop: <{F[g2drop]}>') + + # TODO: main loop should do only + # - pop from pq + # - check if adding edge would block some component + # - add edge + # - call find_option4gate for everyone affected + + # check if (u, v) crosses an existing edge + if delaunay_based: + # look for crossing edges within the neighborhood of (u, v) + # faster, but only works if using the expanded delaunay edges + eX = edge_crossings(u, v, G, diagonals, P) + else: + # when using the edges of a complete graph + # alternate way - slower + eX = [] + eXtmp = [] + eXnodes = set() + nodes2check = set() + uC, vC = VertexC[[u, v]] + for s, t in G.edges: + if s == u or t == u or s == v or t == v or s < 0 or t < 0: + # skip if the edges have a common node or (s, t) is a gate + continue + if is_crossing(uC, vC, *VertexC[[s, t]], touch_is_cross=True): + eXtmp.append((s, t)) + if s in eXnodes: + nodes2check.add(s) + if t in eXnodes: + nodes2check.add(t) + eXnodes.add(s) + eXnodes.add(t) + for s, t in eXtmp: + if s in nodes2check: + for w in G[s]: + if w != t and not is_same_side(uC, vC, + *VertexC[[w, t]]): + eX.append((s, t)) + elif t in nodes2check: + for w in G[t]: + if w != s and not is_same_side(uC, vC, + *VertexC[[w, s]]): + eX.append((s, t)) + else: + eX.append((s, t)) + + if eX: + print(f'<edge_crossing> discarding {(F[u], F[v])}: would cross' + f' {[(F[s], F[t]) for s, t in eX]}') + # abort_edge_addition(g2drop, u, v) + prevented_crossings += 1 + ban_queued_edge(g2drop, u, v) + continue + + g2keep = Gate[v] + root = A.nodes[g2keep]['root'] + + capacity_left = capacity - len(subtrees[u]) - len(subtrees[v]) + + # assess the union's angle span + keepHi = ComponHiLim[g2keep] + keepLo = ComponLoLim[g2keep] + dropHi = ComponHiLim[g2drop] + dropLo = ComponLoLim[g2drop] + newHi = dropHi if angle(*VertexC[[keepHi, root, dropHi]]) > 0 else keepHi + newLo = dropLo if angle(*VertexC[[dropLo, root, keepLo]]) > 0 else keepLo + debug and print(f'<angle_span> //{F[newLo]} : ' + f'{F[newHi]}//') + + # check which gates are within the union's angle span + lR = anglesRank[newLo, root] + hR = anglesRank[newHi, root] + anglesWrap = lR > hR + abort = False + # the more conservative check would be using g2keep instead of + # g2drop in the line below (but then the filter needs changing) + distanceThreshold = d2rootsRank[g2drop, root] + for gate in [g for g in G[root] if + d2rootsRank[g, root] > distanceThreshold]: + gaterank = anglesRank[gate, root] + if (not anglesWrap and (lR < gaterank < hR) or + (anglesWrap and (gaterank > lR or gaterank < hR))): + # possible occlusion of subtree[gate] by union subtree + debug and print(f'<check_occlusion> «{F[u]}-{F[v]}» might ' + f'cross gate <{F[gate]}>') + if gate in Final_G[root]: + if is_crossing_gate(root, gate, u, v, touch_is_cross=True): + abort = True + break + elif gate in ComponIn[g2drop] or gate in ComponIn[g2keep]: + if (len(subtrees[gate]) > capacity_left): + # check crossing with gate + if is_crossing_gate(root, gate, u, v, + touch_is_cross=True): + # find_option for gate, but forbidding g2drop, g2keep + abort = True + break + else: + debug and print(f'$$$$$ UNLIKELY: gate <{F[gate]}> ' + 'could merge with subtree ' + f'<{F[g2keep]}> $$$$$') + else: + # check crossing with next union for gate + entry = pq.tags.get(gate) + if entry is not None: + _, _, _, (s, t) = entry + if is_crossing(*VertexC[[u, v, s, t]], + touch_is_cross=False): + abort = True + break + + if abort: + debug and print(f'######## «{F[u]}-{F[v]}» would block ' + f'gate {F[gate]} ########') + prevented_crossings += 1 + ban_queued_edge(g2drop, u, v) + continue + + # edge addition starts here + subtree = subtrees[v] + subtree |= subtrees[u] + G.remove_edge(A.nodes[u]['root'], g2drop) + log.append((i, 'remE', (A.nodes[u]['root'], g2drop))) + + g2keep_entry = pq.tags.get(g2keep) + if g2keep_entry is not None: + _, _, _, (_, t) = g2keep_entry + # print('node', F[t], 'gate', F[Gate[t]]) + ComponIn[Gate[t]].remove(g2keep) + # TODO: think about why a discard was needed + ComponIn[g2keep].discard(g2drop) + + # update the component's angle span + ComponHiLim[g2keep] = newHi + ComponLoLim[g2keep] = newLo + + # assign root, gate and subtree to the newly added nodes + for n in subtrees[u]: + A.nodes[n]['root'] = root + Gate[n] = g2keep + subtrees[n] = subtree + debug and print(f'<add edge> «{F[u]}-{F[v]}» gate ' + f'<{F[g2keep]}>, ' + f'heap top: <{F[pq[0][-2]]}>, ' + f'«{chr(8211).join([F[x] for x in pq[0][-1]])}»' + f' {pq[0][0]:.1e}' if pq else 'heap EMPTY') + # G.add_edge(u, v, **A.edges[u, v]) + G.add_edge(u, v, length=A[u][v]['length']) + log.append((i, 'addE', (u, v))) + # remove from consideration edges internal to subtrees + A.remove_edge(u, v) + + # finished adding the edge, now check the consequences + if capacity_left > 0: + for gate in list(ComponIn[g2keep]): + if len(subtrees[gate]) > capacity_left: + # TODO: think about why a discard was needed + # ComponIn[g2keep].remove(gate) + ComponIn[g2keep].discard(gate) + # find_option4gate(gate) + # gates2upd8.append(gate) + gates2upd8.add(gate) + for gate in ComponIn[g2drop] - ComponIn[g2keep]: + if len(subtrees[gate]) > capacity_left: + # find_option4gate(gate) + # gates2upd8.append(gate) + gates2upd8.add(gate) + else: + ComponIn[g2keep].add(gate) + # ComponIn[g2drop] = None + # find_option4gate(g2keep) + # gates2upd8.append(g2keep) + gates2upd8.add(g2keep) + else: + # max capacity reached: subtree full + if g2keep in pq.tags: # if required because of i=0 gates + pq.cancel(g2keep) + make_gate_final(root, g2keep) + # don't consider connecting to this full subtree nodes anymore + A.remove_nodes_from(subtree) + for gate in ComponIn[g2drop] | ComponIn[g2keep]: + # find_option4gate(gate) + # gates2upd8.append(gate) + gates2upd8.add(gate) + # ComponIn[g2drop] = None + # ComponIn[g2keep] = None + check_heap4crossings(root, g2keep) + # END: main loop + + if debug: + not_marked = [] + for root in roots: + for gate in G[root]: + if gate not in Final_G[root]: + not_marked.append(gate) + not_marked and print('@@@@ WARNING: gates ' + f'<{", ".join([F[gate] for gate in not_marked])}' + '> were not marked as final @@@@') + + # algorithm finished, store some info in the graph object + G.graph['iterations'] = i + G.graph['prevented_crossings'] = prevented_crossings + G.graph['capacity'] = capacity + G.graph['overfed'] = [len(G[root]) / np.ceil(N / capacity) * M + for root in roots] + G.graph['edges_created_by'] = 'CPEW' + G.graph['edges_fun'] = CPEW + G.graph['creation_options'] = options + G.graph['runtime_unit'] = 's' + G.graph['runtime'] = time.perf_counter() - start_time + return G diff --git a/ed_win/plotting_scripts/NonBranchingEW.py b/ed_win/plotting_scripts/NonBranchingEW.py new file mode 100644 index 0000000..c2c640d --- /dev/null +++ b/ed_win/plotting_scripts/NonBranchingEW.py @@ -0,0 +1,629 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import operator +import time + +import numpy as np + +from .geometric import (angle, apply_edge_exemptions, complete_graph, + delaunay, edge_crossings, is_crossing, + is_same_side) +from .interarraylib import new_graph_like +from .utils import NodeTagger +from .priorityqueue import PriorityQueue + + +F = NodeTagger() + + +def NBEW(G_base, capacity=8, delaunay_based=True, rootlust=0., maxiter=10000, + debug=False, weightfun=None, weight_attr='length'): + '''Non-branching Esau-Williams heuristic for C-MST + inputs: + G_base: networkx.Graph + c: capacity + returns G_cmst: networkx.Graph''' + + start_time = time.perf_counter() + # grab relevant options to store in the graph later + options = dict(delaunay_based=delaunay_based) + + M = G_base.graph['M'] + N = G_base.number_of_nodes() - M + # roots = range(N, N + M) + roots = range(-M, 0) + VertexC = G_base.graph['VertexC'] + d2roots = G_base.graph['d2roots'] + d2rootsRank = G_base.graph['d2rootsRank'] + anglesRank = G_base.graph['anglesRank'] + anglesYhp = G_base.graph['anglesYhp'] + anglesXhp = G_base.graph['anglesXhp'] + + # BEGIN: prepare auxiliary graph with all allowed edges and metrics + if delaunay_based: + A = delaunay(G_base, bind2root=True) + P = A.graph['planar'] + diagonals = A.graph['diagonals'] + # A = delaunay_deprecated(G_base) + # triangles = A.graph['triangles'] + # triangles_exp = A.graph['triangles_exp'] + # apply weightfun on all delaunay edges + if weightfun is not None: + # TODO: fix `apply_edge_exemptions()` for the + # `delaunay()` without triangles + apply_edge_exemptions(A) + # TODO: decide whether to keep this 'else' (to get edge arcs) + # else: + # apply_edge_exemptions(A) + else: + A = complete_graph(G_base) + if weightfun is not None: + options['weightfun'] = weightfun.__name__ + options['weight_attr'] = weight_attr + for u, v, data in A.edges(data=True): + data[weight_attr] = weightfun(data) + # removing root nodes from A to speedup find_option4gate + # this may be done because G already starts with gates + A.remove_nodes_from(roots) + # END: prepare auxiliary graph with all allowed edges and metrics + + # BEGIN: create initial star graph + star_edges = [] + for n in range(N): + root = G_base.nodes[n]['root'] + star_edges.append((root, n, {'weight': d2roots[n, root], + 'length': d2roots[n, root]})) + G = new_graph_like(G_base, star_edges) + # END: create initial star graph + + # BEGIN: helper data structures + + # mappings from nodes + # <subtrees>: maps nodes to the set of nodes in their subtree + subtrees = np.array([{n} for n in range(N)]) + # <Gate>: maps nodes to their gates + Gate = np.array([n for n in range(N)]) + # <Tail>: maps nodes to their component's tail + Tail = np.array([n for n in range(N)]) + + # mappings from components (identified by their gates) + # <ComponIn>: maps component to set of components queued to merge in + ComponIn = np.array([set() for _ in range(N)]) + ComponLoLim = np.arange(N) # most CW node + ComponHiLim = np.arange(N) # most CCW node + + # mappings from roots + # <Final_G>: set of gates of finished components (one set per root) + Final_G = np.array([set() for _ in range(M)]) + + # other structures + # <pq>: queue prioritized by lowest tradeoff length + pq = PriorityQueue() + # find_option4gate() + # <gates2upd8>: deque for components that need to go through + # gates2upd8 = deque() + gates2upd8 = set() + gates2retry = [] + # <edges2ban>: deque for edges that should not be considered anymore + # edges2ban = deque() + # TODO: this is not being used, decide what to do about it + edges2ban = set() + # TODO: edges{N,C,V} could be used to vectorize the edge crossing detection + # <edgesN>: array of nodes of the edges of G (N×2) + # <edgesC>: array of node coordinates for edges of G (N×2×2) + # <edgesV>: array of vectors of the edges of G (N×2) + # <i>: iteration counter + i = 0 + # <prevented_crossing>: counter for edges discarded due to crossings + prevented_crossings = 0 + # END: helper data structures + + def is_rank_within(rank, lowRank, highRank, anglesWrap, + touch_is_cross=False): + less = operator.le if touch_is_cross else operator.lt + if anglesWrap: + return less(rank, lowRank) or less(highRank, rank) + else: + return less(lowRank, rank) and less(rank, highRank) + + def is_crossing_gate(root, gate, u, v, touch_is_cross=False): + '''choices for `less`: + -> operator.lt: touching is not crossing + -> operator.le: touching is crossing''' + gaterank = anglesRank[gate, root] + uR, vR = anglesRank[u, root], anglesRank[v, root] + highRank, lowRank = (uR, vR) if uR >= vR else (vR, uR) + Xhp = anglesXhp[[u, v], root] + uYhp, vYhp = anglesYhp[[u, v], root] + if is_rank_within(gaterank, lowRank, highRank, + not any(Xhp) and uYhp != vYhp, touch_is_cross): + if not is_same_side(*VertexC[[u, v, root, gate]]): + # crossing gate + debug and print(f'<crossing> discarding ' + f'«{F[u]}–{F[v]}»: would cross' + f'gate <{F[gate]}>') + return True + return False + + def make_gate_final(root, g2keep): + Final_G[root].add(g2keep) + log.append((i, 'finalG', (g2keep, root))) + debug and print(f'<final> gate ' + f'[{F[g2keep]}] added') + + def component_merging_choices(gate, forbidden=None): + # gather all the edges leaving the subtree of gate + if forbidden is None: + forbidden = set() + forbidden.add(gate) + d2root = d2roots[gate, A.nodes[gate]['root']] + capacity_left = capacity - len(subtrees[gate]) + root_lust = rootlust * len(subtrees[gate]) / capacity + weighted_edges = [] + edges2discard = [] + for u in set((gate, Tail[gate])): + for v in A[u]: + if (Gate[v] in forbidden or + len(subtrees[v]) > capacity_left): + # useless edges + edges2discard.append((u, v)) + elif v != Tail[v]: + if v != Gate[v]: + # useless edges + edges2discard.append((u, v)) + else: + W = A[u][v][weight_attr] + # if W <= d2root: # TODO: what if I use <= instead of <? + if W < d2root: + d2rGain = d2root - d2roots[Gate[v], A.nodes[Gate[v]]['root']] + # useful edges + tiebreaker = d2rootsRank[v, A[u][v]['root']] + weighted_edges.append((W - d2rGain * root_lust, + tiebreaker, u, v)) + # weighted_edges.append((W, tiebreaker, u, v)) + return weighted_edges, edges2discard + + def sort_union_choices(weighted_edges): + # this function could be outside esauwilliams() + unordchoices = np.array( + weighted_edges, + dtype=[('weight', np.float64), + ('vd2rootR', np.int_), + ('u', np.int_), + ('v', np.int_)]) + # result = np.argsort(unordchoices, order=['weight']) + # unordchoices = unordchoices[result] + + # DEVIATION FROM Esau-Williams + # rounding of weight to make ties more likely + # tie-breaking by proximity of 'v' node to root + # purpose is to favor radial alignment of components + tempchoices = unordchoices.copy() + tempchoices['weight'] /= tempchoices['weight'].min() + tempchoices['weight'] = (20 * tempchoices['weight']).round() # 5% + + result = np.argsort(tempchoices, order=['weight', 'vd2rootR']) + choices = unordchoices[result] + return choices + + def first_non_crossing(choices, gate): + '''go through choices and return the first that does not cross a final + gate''' + # TODO: remove gate from the parameters + nonlocal prevented_crossings + found = False + # BEGIN: for loop that picks an edge + for weight, tiebreaker, u, v in choices: + found = True + root = A[u][v]['root'] + + # check if a gate is crossing the edge (u, v) + # TODO: this only looks at the gates connecting to the edges' + # closest root , is it relevant to look at all roots? + # PendingG = set() + + for finalG in Final_G[root]: + # TODO: test a gate exactly overlapping with a node + # Elaborating: angleRank will take care of this corner case. + # the gate will fall within one of the edges around the node + if is_crossing_gate(root, finalG, u, v): + # crossing gate, discard edge + prevented_crossings += 1 + # TODO: call ban_queued_edge (problem: these edges are not + # queued) + if (u, v) in A.edges: + A.remove_edge(u, v) + else: + debug and print('<<<<<< UNLIKELY.A first_non_crossing' + f'(): ({F[u]}, {F[v]}) not in ' + 'A.edges >>>>') + if Gate[v] in ComponIn[gate]: + # this means the target component was in line to + # connect to the current component + debug and print('<<<<<< UNLIKELY.B first_non_crossing' + f'(): Gate[{F[v]}] in ComponIn[' + f'{F[gate]}] >>>>>>>') + _, _, _, (s, t) = pq.tags.get(Gate[v]) + if t == u: + ComponIn[gate].remove(Gate[v]) + # PendingG.add(Gate[v]) + # gates2upd8.append(Gate[v]) + gates2upd8.add(Gate[v]) + + found = False + break + # for pending in PendingG: + # print(f'<pending> processing ' + # f'pending [{F[pending]}]') + # find_option4gate(pending) + if found: + break + # END: for loop that picks an edge + return (weight, u, v) if found else () + + def find_option4gate(gate): + debug and print(f'<find_option4gate> starting... gate = ' + f'<{F[gate]}>') + if edges2ban: + debug and print(f'<<<<<<<edges2ban>>>>>>>>>>> _{len(edges2ban)}_') + while edges2ban: + # edge2ban = edges2ban.popleft() + edge2ban = edges2ban.pop() + ban_queued_edge(*edge2ban) + # () get component expansion edges with weight + weighted_edges, edges2discard = component_merging_choices(gate) + # discard useless edges + A.remove_edges_from(edges2discard) + # () sort choices + choices = sort_union_choices(weighted_edges) if weighted_edges else [] + # () check gate crossings + choice = first_non_crossing(choices, gate) + if choice: + # merging is better than gate, submit entry to pq + weight, u, v = choice + # tradeoff calculation + tradeoff = weight - d2roots[gate, A.nodes[gate]['root']] + pq.add(tradeoff, gate, (u, v)) + ComponIn[Gate[v]].add(gate) + debug and print(f'<pushed> g2drop <{F[gate]}>, ' + f'«{F[u]}–{F[v]}», tradeoff = {tradeoff:.1e}') + else: + # no viable edge is better than gate for this node + # this becomes a final gate + if i: # run only if not at i = 0 + # definitive gates at iteration 0 do not cross any other edges + # they are not included in Final_G because the algorithm + # considers the gates extending to infinity (not really) + if len(A.edges(gate)): + # there is at least one usable edge + # maybe its target will become a tail later + gates2retry.append(gate) + else: + root = A.nodes[gate]['root'] + make_gate_final(root, gate) + check_heap4crossings(root, gate) + debug and print('<cancelling>', F[gate]) + if gate in pq.tags: + # i=0 gates and check_heap4crossings reverse_entry + # may leave accepting gates out of pq + pq.cancel(gate) + + def check_heap4crossings(root, finalG): + '''search the heap for edges that cross the gate 'finalG'. + calls find_option4gate for each of the subtrees involved''' + for tradeoff, _, g2drop, uv in pq: + # if uv is None or uv not in A.edges: + if uv is None: + continue + u, v = uv + if is_crossing_gate(root, finalG, u, v): + nonlocal prevented_crossings + # crossing gate, discard edge + prevented_crossings += 1 + ban_queued_edge(g2drop, u, v) + + def ban_queued_edge(g2drop, u, v, remove_from_A=True): + if ((u, v) in A.edges) and remove_from_A: + A.remove_edge(u, v) + else: + debug and print('<<<< UNLIKELY <ban_queued_edge()> ' + f'({F[u]}, {F[v]}) not in A.edges >>>>') + g2keep = Gate[v] + # TODO: think about why a discard was needed + ComponIn[g2keep].discard(g2drop) + # gates2upd8.appendleft(g2drop) + gates2upd8.add(g2drop) + # find_option4gate(g2drop) + + # BEGIN: block to be simplified + is_reverse = False + componin = g2keep in ComponIn[g2drop] + reverse_entry = pq.tags.get(g2keep) + if reverse_entry is not None: + _, _, _, (s, t) = reverse_entry + if (t, s) == (u, v): + # TODO: think about why a discard was needed + ComponIn[g2drop].discard(g2keep) + # this is assymetric on purpose (i.e. not calling + # pq.cancel(g2drop), because find_option4gate will do) + pq.cancel(g2keep) + find_option4gate(g2keep) + is_reverse = True + + # if this if is not visited, replace the above with ComponIn check + # this means that if g2keep is to also merge with g2drop, then the + # edge of the merging must be (v, u) + if componin != is_reverse: + print(f'«{F[u]}–{F[v]}», ' + f'g2drop <{F[g2drop]}>, g2keep <{F[g2keep]}> ' + f'componin: {componin}, is_reverse: {is_reverse}') + + # END: block to be simplified + + # TODO: check if this function is necessary (not used) + def abort_edge_addition(g2drop, u, v): + if (u, v) in A.edges: + A.remove_edge(u, v) + else: + print('<<<< UNLIKELY <abort_edge_addition()> ' + f'({F[u]}, {F[v]}) not in A.edges >>>>') + ComponIn[Gate[v]].remove(g2drop) + find_option4gate(g2drop) + + # initialize pq + for n in range(N): + find_option4gate(n) + + log = [] + G.graph['log'] = log + loop = True + # BEGIN: main loop + while loop: + i += 1 + if i > maxiter: + print(f'ERROR: maxiter reached ({i})') + break + debug and print(f'[{i}]') + # debug and print(f'[{i}] bj–bm root: {A.edges[(F.bj, F.bm)]["root"]}') + if gates2upd8: + debug and print('gates2upd8:', ', '.join(F[gate] for gate in + gates2upd8)) + retrylist = gates2retry.copy() + gates2retry.clear() + for gate in retrylist: + if gate in A: + find_option4gate(gate) + while gates2upd8: + # find_option4gate(gates2upd8.popleft()) + find_option4gate(gates2upd8.pop()) + if not pq: + # finished + break + g2drop, (u, v) = pq.top() + debug and print(f'<popped> «{F[u]}–{F[v]}»,' + f' g2drop: <{F[g2drop]}>') + + # TODO: main loop should do only + # - pop from pq + # - check if adding edge would block some component + # - add edge + # - call find_option4gate for everyone affected + + # check if (u, v) crosses an existing edge + if delaunay_based: + # look for crossing edges within the neighborhood of (u, v) + # faster, but only works if using the expanded delaunay edges + # eX = edge_crossings(u, v, G, triangles, triangles_exp) + eX = edge_crossings(u, v, G, diagonals, P) + else: + # when using the edges of a complete graph + # alternate way - slower + eX = [] + eXtmp = [] + eXnodes = set() + nodes2check = set() + uC, vC = VertexC[[u, v]] + for s, t in G.edges: + if s == u or t == u or s == v or t == v or s < 0 or t < 0: + # skip if the edges have a common node or (s, t) is a gate + continue + if is_crossing(uC, vC, *VertexC[[s, t]], touch_is_cross=True): + eXtmp.append((s, t)) + if s in eXnodes: + nodes2check.add(s) + if t in eXnodes: + nodes2check.add(t) + eXnodes.add(s) + eXnodes.add(t) + for s, t in eXtmp: + if s in nodes2check: + for w in G[s]: + if w != t and not is_same_side(uC, vC, + *VertexC[[w, t]]): + eX.append((s, t)) + elif t in nodes2check: + for w in G[t]: + if w != s and not is_same_side(uC, vC, + *VertexC[[w, s]]): + eX.append((s, t)) + else: + eX.append((s, t)) + + if eX: + print(f'<edge_crossing> discarding {(F[u], F[v])}: would cross' + f' {[(F[s], F[t]) for s, t in eX]}') + # abort_edge_addition(g2drop, u, v) + prevented_crossings += 1 + ban_queued_edge(g2drop, u, v) + continue + + if v != Tail[v]: + # find_option4gate(g2drop) + ban_queued_edge(g2drop, u, v, remove_from_A=False) + continue + + g2keep = Gate[v] + root = A.nodes[g2keep]['root'] + + capacity_left = capacity - len(subtrees[u]) - len(subtrees[v]) + + # assess the union's angle span + keepHi = ComponHiLim[g2keep] + keepLo = ComponLoLim[g2keep] + dropHi = ComponHiLim[g2drop] + dropLo = ComponLoLim[g2drop] + newHi = dropHi if angle(*VertexC[[keepHi, root, dropHi]]) > 0 else keepHi + newLo = dropLo if angle(*VertexC[[dropLo, root, keepLo]]) > 0 else keepLo + debug and print(f'<angle_span> //{F[newLo]} : ' + f'{F[newHi]}//') + + # check which gates are within the union's angle span + lR = anglesRank[newLo, root] + hR = anglesRank[newHi, root] + anglesWrap = lR > hR + abort = False + # the more conservative check would be using g2keep instead of + # g2drop in the line below (but then the filter needs changing) + distanceThreshold = d2rootsRank[g2drop, root] + for gate in [g for g in G[root] if + d2rootsRank[g, root] > distanceThreshold]: + gaterank = anglesRank[gate, root] + if (not anglesWrap and (lR < gaterank < hR) or + (anglesWrap and (gaterank > lR or gaterank < hR))): + # possible occlusion of subtree[gate] by union subtree + debug and print(f'<check_occlusion> «{F[u]}-{F[v]}» might ' + f'cross gate <{F[gate]}>') + if gate in Final_G[root]: + if is_crossing_gate(root, gate, u, v, touch_is_cross=True): + abort = True + break + elif gate in ComponIn[g2drop] or gate in ComponIn[g2keep]: + if (len(subtrees[gate]) > capacity_left): + # check crossing with gate + if is_crossing_gate(root, gate, u, v, + touch_is_cross=True): + # find_option for gate, but forbidding g2drop, g2keep + abort = True + break + else: + debug and print(f'$$$$$ UNLIKELY: gate <{F[gate]}> ' + 'could merge with subtree ' + f'<{F[g2keep]}> $$$$$') + else: + # check crossing with next union for gate + entry = pq.tags.get(gate) + if entry is not None: + _, _, _, (s, t) = entry + if is_crossing(*VertexC[[u, v, s, t]], + touch_is_cross=False): + abort = True + break + + if abort: + debug and print(f'######## «{F[u]}-{F[v]}» would block ' + f'gate {F[gate]} ########') + prevented_crossings += 1 + ban_queued_edge(g2drop, u, v) + continue + + # edge addition starts here + # newTail = Tail[g2drop] if u == g2drop else Tail[u] + newTail = Tail[g2drop] if u == g2drop else g2drop + for n in subtrees[v]: + Tail[n] = newTail + + subtree = subtrees[v] + subtree |= subtrees[u] + G.remove_edge(A.nodes[u]['root'], g2drop) + log.append((i, 'remE', (A.nodes[u]['root'], g2drop))) + + g2keep_entry = pq.tags.get(g2keep) + if g2keep_entry is not None: + _, _, _, (_, t) = g2keep_entry + # print('node', F[t], 'gate', F[Gate[t]]) + ComponIn[Gate[t]].remove(g2keep) + # TODO: think about why a discard was needed + ComponIn[g2keep].discard(g2drop) + + # update the component's angle span + ComponHiLim[g2keep] = newHi + ComponLoLim[g2keep] = newLo + + if u != g2drop: + for n in subtrees[u]: + Tail[n] = newTail + # assign root, gate and subtree to the newly added nodes + for n in subtrees[u]: + A.nodes[n]['root'] = root + Gate[n] = g2keep + subtrees[n] = subtree + debug and print(f'<add edge> «{F[u]}-{F[v]}» gate ' + f'<{F[g2keep]}>, ' + f'heap top: <{F[pq[0][-2]]}>, ' + f'«{chr(8211).join([F[x] for x in pq[0][-1]])}»' + f' {pq[0][0]:.1e}' if pq else 'heap EMPTY') + G.add_edge(u, v, length=A[u][v]['length']) + log.append((i, 'addE', (u, v))) + # remove from consideration edges internal to subtrees + A.remove_edge(u, v) + + # finished adding the edge, now check the consequences + if capacity_left > 0: + for gate in list(ComponIn[g2keep]): + if len(subtrees[gate]) > capacity_left: + # TODO: think about why a discard was needed + # ComponIn[g2keep].remove(gate) + ComponIn[g2keep].discard(gate) + # find_option4gate(gate) + # gates2upd8.append(gate) + gates2upd8.add(gate) + for gate in ComponIn[g2drop] - ComponIn[g2keep]: + if len(subtrees[gate]) > capacity_left: + # find_option4gate(gate) + # gates2upd8.append(gate) + gates2upd8.add(gate) + else: + ComponIn[g2keep].add(gate) + # ComponIn[g2drop] = None + # find_option4gate(g2keep) + # gates2upd8.append(g2keep) + gates2upd8.add(g2keep) + else: + # max capacity reached: subtree full + if g2keep in pq.tags: # if required because of i=0 gates + pq.cancel(g2keep) + make_gate_final(root, g2keep) + # don't consider connecting to this full subtree nodes anymore + A.remove_nodes_from(subtree) + for gate in ComponIn[g2drop] | ComponIn[g2keep]: + # find_option4gate(gate) + # gates2upd8.append(gate) + gates2upd8.add(gate) + # ComponIn[g2drop] = None + # ComponIn[g2keep] = None + check_heap4crossings(root, g2keep) + # END: main loop + + if debug: + not_marked = [] + for root in roots: + for gate in G[root]: + if gate not in Final_G[root]: + not_marked.append(gate) + not_marked and print('@@@@ WARNING: gates ' + f'<{", ".join([F[gate] for gate in not_marked])}' + '> were not marked as final @@@@') + + # algorithm finished, store some info in the graph object + G.graph['iterations'] = i + G.graph['prevented_crossings'] = prevented_crossings + G.graph['capacity'] = capacity + G.graph['overfed'] = [len(G[root]) / np.ceil(N / capacity) * M + for root in roots] + G.graph['edges_created_by'] = 'NBEW' + G.graph['edges_fun'] = NBEW + G.graph['creation_options'] = options + G.graph['runtime_unit'] = 's' + G.graph['runtime'] = time.perf_counter() - start_time + return G diff --git a/ed_win/plotting_scripts/ObstacleBypassingEW.py b/ed_win/plotting_scripts/ObstacleBypassingEW.py new file mode 100644 index 0000000..ce292d0 --- /dev/null +++ b/ed_win/plotting_scripts/ObstacleBypassingEW.py @@ -0,0 +1,1280 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import operator +import time +from collections import defaultdict + +import networkx as nx +import numpy as np +from scipy.spatial.distance import cdist + +from .geometric import (angle, apply_edge_exemptions, delaunay, + edge_crossings, is_bunch_split_by_corner, is_crossing, + is_same_side) +from .interarraylib import new_graph_like +from .utils import Alerter, NodeStr, NodeTagger +from .priorityqueue import PriorityQueue + + +F = NodeTagger() + + +def OBEW(G_base, capacity=8, rootlust=None, maxiter=10000, maxDepth=4, + MARGIN=1e-4, debug=False, warnwhere=None, weightfun=None, + weight_attr='length'): + '''Obstacle Bypassing Esau-Williams heuristic for C-MST + inputs: + G_base: networkx.Graph + c: capacity + returns G_cmst: networkx.Graph + + warnwhere is for printing debugging info based on the iteration number and + function name (see + utils.Alerter)''' + + start_time = time.perf_counter() + + if warnwhere is not None: + warn = Alerter(warnwhere, 'i') + else: # could check if debug is True and make warn = print + def warn(*args, **kwargs): + pass + + # rootlust_formula = '0.7*(cur_capacity/(capacity - 1))**2' + # rootlust_formula = f'0.6*cur_capacity/capacity' + # rootlust_formula = f'0.6*(cur_capacity + 1)/capacity' + # rootlust_formula = f'0.6*cur_capacity/(capacity - 1)' + + if rootlust is None: + def rootlustfun(_): + return 0. + else: + rootlustfun = eval('lambda cur_capacity: ' + rootlust, locals()) + # rootlust = lambda cur_capacity: 0.7*(cur_capacity/(capacity - 1))**2 + + # save relevant options to store in the graph later + options = dict(MARGIN=MARGIN, variant='C', + rootlust=rootlust) + + M = G_base.graph['M'] + N = G_base.number_of_nodes() - M + G_base.graph['N'] = N + # SiteC = G_base.graph['VertexC'] + # VertexC = np.vstack((SiteC[:N], + # np.zeros((D, 2), dtype=SiteC.dtype), + # SiteC[N+D:])) + roots = range(-M, 0) + + # list of variables indexed by vertex id: + # d2roots, d2rootsRank, anglesRank, anglesYhp, anglesXhp + # Subtree, VertexC + # list of variables indexed by subtree id: + # CompoIn, CompoLolim, CompoHilim + # dicts keyed by subtree id + # DetourHop, detourLoNotHi + # sets of subtree ids: + # Final_G + # + # need to have a table of vertex -> gate node + + d2roots = G_base.graph['d2roots'] + d2rootsRank = G_base.graph['d2rootsRank'] + anglesRank = G_base.graph['anglesRank'] + anglesYhp = G_base.graph['anglesYhp'] + anglesXhp = G_base.graph['anglesXhp'] + # TODO: do away with pre-calculated crossings + # Xings = G_base.graph['crossings'] + + # crossings = G_base.graph['crossings'] + # BEGIN: prepare auxiliary graph with all allowed edges and metrics + A = delaunay(G_base, bind2root=True) + P = A.graph['planar'] + diagonals = A.graph['diagonals'] + # A = delaunay_deprecated(G_base) + # triangles = A.graph['triangles'] + # triangles_exp = A.graph['triangles_exp'] + # apply weightfun on all delaunay edges + if weightfun is not None: + # TODO: fix `apply_edge_exemptions()` for the + # `delaunay()` without triangles + apply_edge_exemptions(A) + options['weightfun'] = weightfun.__name__ + options['weight_attr'] = weight_attr + for u, v, data in A.edges(data=True): + data[weight_attr] = weightfun(data) + # removing root nodes from A to speedup find_option4gate + # this may be done because G already starts with gates + A.remove_nodes_from(roots) + # END: prepare auxiliary graph with all allowed edges and metrics + + # BEGIN: create initial star graph + star_edges = [] + for n in range(N): + root = G_base.nodes[n]['root'] + star_edges.append((root, n, {'length': d2roots[n, root]})) + G = new_graph_like(G_base, star_edges) + # END: create initial star graph + + # BEGIN: helper data structures + + # upper estimate number of Detour nodes: + # Dmax = round(2*N/3/capacity) + Dmax = N + + # mappings from nodes + # <Subtree>: maps nodes to the set of nodes in their subtree + Subtree = np.empty((N + Dmax,), dtype=object) + Subtree[:N] = [{n} for n in range(N)] + # Subtree = np.array([{n} for n in range(N)]) + # TODO: fnT might be better named Pof (Prime of) + # <fnT>: farm node translation table + # to be used when indexing: VertexC, d2roots, angles, etc + # fnT[-M..(N+Dmax)] -> -M..N + fnT = np.arange(N + Dmax + M) + fnT[-M:] = range(-M, 0) + + # <Stale>: list of detour nodes that were discarded + Stale = [] + + # this is to make fnT available for plot animation + # a new, trimmed, array be assigned after the heuristic is done + G.graph['fnT'] = fnT + + n2s = NodeStr(fnT, N) + # <gnT>: gate node translation table + # to be used when indexing Subtree[] + # gnT[-M..(N+Dmax)] -> -M..N + gnT = np.arange(N + Dmax) + + # mappings from components (identified by their gates) + # <ComponIn>: maps component to set of components queued to merge in + ComponIn = np.array([set() for _ in range(N)]) + ComponLoLim = np.hstack((np.arange(N)[:, np.newaxis],) * M) # most CW node + ComponHiLim = ComponLoLim.copy() # most CW node + # ComponLoLim = np.arange(N) # most CCW node + # ComponHiLim = np.arange(N) # most CCW node + + # mappings from roots + # <Final_G>: set of gates of finished components (one set per root) + Final_G = np.array([set() for _ in range(M)]) + + # other structures + # <pq>: queue prioritized by lowest tradeoff length + pq = PriorityQueue() + # find_option4gate() + # <gates2upd8>: deque for components that need to go through + # gates2upd8 = deque() + gates2upd8 = set() + # <edges2ban>: deque for edges that should not be considered anymore + # edges2ban = deque() + # TODO: this is not being used, decide what to do about it + edges2ban = set() + VertexC = G_base.graph['VertexC'] + # SiteC = G_base.graph['VertexC'] + # VertexC = np.vstack((SiteC[:N], + # np.full((Dmax, 2), np.nan, dtype=SiteC.dtype), + # SiteC[N:])) + # number of Detour nodes added + D = 0 + # <DetourHop>: maps gate nodes to a list of nodes of the Detour path + # (root is not on the list) + DetourHop = defaultdict(list) + detourLoNotHi = dict() + # detour = defaultdict(list) + # detouroverlaps = {} + + # <i>: iteration counter + i = 0 + # <prevented_crossing>: counter for edges discarded due to crossings + prevented_crossings = 0 + # END: helper data structures + + def is_rank_within(rank, lowRank, highRank, anglesWrap, + touch_is_cross=False): + less = operator.le if touch_is_cross else operator.lt + if anglesWrap: + return less(rank, lowRank) or less(highRank, rank) + else: + return less(lowRank, rank) and less(rank, highRank) + + def is_crossing_gate(root, gate, u, v, touch_is_cross=False): + '''choices for `less`: + -> operator.lt: touching is not crossing + -> operator.le: touching is crossing''' + # get the primes of all nodes + gate_, u_, v_ = fnT[[gate, u, v]] + gaterank = anglesRank[gate_, root] + uR, vR = anglesRank[u_, root], anglesRank[v_, root] + highRank, lowRank = (uR, vR) if uR >= vR else (vR, uR) + Xhp = anglesXhp[[u_, v_], root] + uYhp, vYhp = anglesYhp[[u_, v_], root] + if is_rank_within(gaterank, lowRank, highRank, + not any(Xhp) and uYhp != vYhp, touch_is_cross): + # TODO: test the funtion's touch_is_cross in the call below + if not is_same_side(*VertexC[[u_, v_, root, gate_]]): + # crossing gate + debug and print(f'<is_crossing_gate> {n2s(u, v)}: would ' + f'cross gate {n2s(gate)}') + return True + return False + + def make_gate_final(root, g2keep): + if g2keep not in Final_G[root]: + Final_G[root].add(g2keep) + log.append((i, 'finalG', (g2keep, root))) + debug and print(f'<make_gate_final> GATE {n2s(g2keep, root)} added.') + + def component_merging_choices_plain(gate, forbidden=None): + # gather all the edges leaving the subtree of gate + if forbidden is None: + forbidden = set() + forbidden.add(gate) + d2root = d2roots[fnT[gate], G.nodes[gate]['root']] + capacity_left = capacity - len(Subtree[gate]) + weighted_edges = [] + edges2discard = [] + for u in Subtree[gate]: + for v in A[u]: + if (gnT[v] in forbidden or + len(Subtree[v]) > capacity_left): + # useless edges + edges2discard.append((u, v)) + else: + # newd2root = d2roots[fnT[gnT[v]], G.nodes[fnT[v]]['root']] + W = A[u][v][weight_attr] + # if W <= d2root: # TODO: what if I use <= instead of <? + if W < d2root: + # useful edges + tiebreaker = d2rootsRank[fnT[v], A[u][v]['root']] + weighted_edges.append((W, tiebreaker, u, v)) + # weighted_edges.append((W-(d2root - newd2root)/3, + # tiebreaker, u, v)) + return weighted_edges, edges2discard + + def component_merging_choices(gate, forbidden=None): + # gather all the edges leaving the subtree of gate + if forbidden is None: + forbidden = set() + forbidden.add(gate) + root = G.nodes[gate]['root'] + d2root = d2roots[gate, root] + capacity_left = capacity - len(Subtree[gate]) + root_lust = rootlustfun(len(Subtree[gate])) + weighted_edges = [] + edges2discard = [] + for u in Subtree[gate]: + for v in A[u]: + if (gnT[v] in forbidden or + len(Subtree[v]) > capacity_left): + # useless edges + edges2discard.append((u, v)) + else: + d2rGain = d2root - d2roots[gnT[v], G.nodes[fnT[v]]['root']] + W = A[u][v][weight_attr] + # if W <= d2root: # TODO: what if I use <= instead of <? + if W < d2root: + # useful edges + tiebreaker = d2rootsRank[fnT[v], A[u][v]['root']] + # weighted_edges.append((W, tiebreaker, u, v)) + weighted_edges.append((W - d2rGain * root_lust, + tiebreaker, u, v)) + return weighted_edges, edges2discard + + def sort_union_choices(weighted_edges): + # this function could be outside esauwilliams() + unordchoices = np.array( + weighted_edges, + dtype=[('weight', np.float64), + ('vd2rootR', np.int_), + ('u', np.int_), + ('v', np.int_)]) + # result = np.argsort(unordchoices, order=['weight']) + # unordchoices = unordchoices[result] + + # DEVIATION FROM Esau-Williams + # rounding of weight to make ties more likely + # tie-breaking by proximity of 'v' node to root + # purpose is to favor radial alignment of components + tempchoices = unordchoices.copy() + tempchoices['weight'] /= tempchoices['weight'].min() + tempchoices['weight'] = (20 * tempchoices['weight']).round() # 5% + + result = np.argsort(tempchoices, order=['weight', 'vd2rootR']) + choices = unordchoices[result] + return choices + + def find_option4gate(gate): + debug and i and print(f'<find_option4gate> starting... gate = ' + f'<{F[gate]}>') + if edges2ban: + debug and print(f'<<<<<<<edges2ban>>>>>>>>>>> _{len(edges2ban)}_') + while edges2ban: + # edge2ban = edges2ban.popleft() + edge2ban = edges2ban.pop() + ban_queued_edge(*edge2ban) + # () get component expansion edges with weight + weighted_edges, edges2discard = component_merging_choices(gate) + # discard useless edges + A.remove_edges_from(edges2discard) + # () sort choices + choices = sort_union_choices(weighted_edges) if weighted_edges else [] + if len(choices) > 0: + choice = choices[0] + # merging is better than gate, submit entry to pq + # weight, u, v = choice + weight, _, u, v = choice + # tradeoff calculation + tradeoff = weight - d2roots[fnT[gate], A.nodes[gate]['root']] + pq.add(tradeoff, gate, (u, v)) + ComponIn[gnT[v]].add(gate) + debug and i and print( + f'<find_option4gate> pushed {n2s(u, v)}, g2drop ' + f'<{F[gate]}>, tradeoff = {-tradeoff:.0f}') + else: + # no viable edge is better than gate for this node + # this becomes a final gate + if i: # run only if not at i = 0 + # definitive gates at iteration 0 do not cross any other edges + # they are not included in Final_G because the algorithm + # considers the gates extending to infinity (not really) + root = A.nodes[gate]['root'] + make_gate_final(root, gate) + # check_heap4crossings(root, gate) + debug and print('<cancelling>', F[gate]) + if gate in pq.tags: + # i=0 gates and check_heap4crossings reverse_entry + # may leave accepting gates out of pq + pq.cancel(gate) + + def ban_queued_edge(g2drop, u, v): + if (u, v) in A.edges: + A.remove_edge(u, v) + else: + debug and print('<<<< UNLIKELY <ban_queued_edge()> ' + f'({F[u]}, {F[v]}) not in A.edges >>>>') + g2keep = gnT[v] + # TODO: think about why a discard was needed + ComponIn[g2keep].discard(g2drop) + # gates2upd8.appendleft(g2drop) + gates2upd8.add(g2drop) + # find_option4gate(g2drop) + + # BEGIN: block to be simplified + is_reverse = False + componin = g2keep in ComponIn[g2drop] + reverse_entry = pq.tags.get(g2keep) + if reverse_entry is not None: + _, _, _, (s, t) = reverse_entry + if (t, s) == (u, v): + # TODO: think about why a discard was needed + ComponIn[g2drop].discard(g2keep) + # this is assymetric on purpose (i.e. not calling + # pq.cancel(g2drop), because find_option4gate will do) + pq.cancel(g2keep) + find_option4gate(g2keep) + is_reverse = True + + # if this if is not visited, replace the above with ComponIn check + # this means that if g2keep is to also merge with g2drop, then the + # edge of the merging must be (v, u) + if componin != is_reverse: + # this does happen sometimes (componin: True, is_reverse: False) + debug and print(f'{n2s(u, v)}, ' + f'g2drop <{F[g2drop]}>, g2keep <{F[g2keep]}> ' + f'componin: {componin}, is_reverse: {is_reverse}') + + # END: block to be simplified + + # TODO: check if this function is necessary (not used) + def abort_edge_addition(g2drop, u, v): + if (u, v) in A.edges: + A.remove_edge(u, v) + else: + print('<<<< UNLIKELY <abort_edge_addition()> ' + f'{n2s(u, v)} not in A.edges >>>>') + ComponIn[gnT[v]].remove(g2drop) + find_option4gate(g2drop) + + # TODO: check if this function is necessary (not used) + def get_subtrees_closest_node(gate, origin): + componodes = list(Subtree[gate]) + if len(componodes) > 1: + dist = np.squeeze(cdist(VertexC[fnT[componodes]], + VertexC[np.newaxis, fnT[origin]])) + else: + dist = np.array([np.hypot(*(VertexC[fnT[componodes[0]]] - + VertexC[np.newaxis, fnT[origin]]).T)]) + idx = np.argmin(dist) + closest = componodes[idx] + return closest + + def get_crossings(s, t, detour_waiver=False): + '''generic crossings checker + common node is not crossing''' + s_, t_ = fnT[[s, t]] + # st = frozenset((s_, t_)) + st = (s_, t_) if s_ < t_ else (t_, s_) + # if st in triangles or st in triangles_exp: + if st in P.edges or st in diagonals: + # <(s_, t_) is in the expanded Delaunay edge set> + # Xlist = edge_crossings(s_, t_, G, triangles, triangles_exp) + Xlist = edge_crossings(s_, t_, G, diagonals, P) + # crossings with expanded Delaunay already checked + # just detour edges missing + nbunch = list(range(N, N + D)) + else: + # <(s, t) is not in the expanded Delaunay edge set> + Xlist = [] + nbunch = None # None means all nodes + sC, tC = VertexC[[s_, t_]] + # st_is_detour = s >= N or t >= N + for w_x in G.edges(nbunch): + w, x = w_x + w_, x_ = fnT[[w, x]] + # both_detours = st_is_detour and (w >= N or x >= N) + skip = detour_waiver and (w >= N or x >= N) + if skip or s_ == w_ or t_ == w_ or s_ == x_ or t_ == x_: + # <edges have a common node> + continue + if is_crossing(sC, tC, *VertexC[[w_, x_]], touch_is_cross=True): + Xlist.append(w_x) + return Xlist + + def get_crossings_deprecated(s, t): + # TODO: THIS RELIES ON precalculated crossings + sC, tC = VertexC[fnT[[s, t]]] + rootC = VertexC[-M:] + if np.logical_or.reduce(((sC - rootC) * (tC - rootC)).sum(axis=1) < 0): + # pre-calculation pruned edges with more than 90° angle + # so the output will be equivalent to finding a crossings + return (None,) + Xlist = [] + for (w, x) in Xings[frozenset(fnT[[s, t]])]: + if G.has_edge(w, x): + # debug and print(f'{n2s(w, x)} crosses {n2s(s, t)}') + Xlist.append((w, x)) + return Xlist + + def plan_detour(root, blocked, goal_, u, v, barrierLo, + barrierHi, savings, depth=0, remove=set()): + ''' + (blocked, goal_) is the detour segment + (u, v) is an edge crossing it + barrierLo/Hi are the extremes of the subtree of (u, v) wrt root + tradeoff = <benefit of the edge addition> - <previous detours> + ''' + # print(f'[{i}] <plan_detour_recursive[{depth}]> {n2s(u, v)} ' + # f'blocking {n2s(blocked, goal_)}') + goalC = VertexC[goal_] + cleatnode = gnT[blocked] + detourHop = DetourHop[cleatnode] + blocked_ = fnT[blocked] + warn(f'({depth}) ' + + ('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n' if depth == 0 + else '') + + f'{n2s(u, v)} blocks {n2s(blocked)}, ' + f'gate: {n2s(cleatnode)}') + + # <refL>: length of the edge crossed by (u, v) – reference of cost + if goal_ < 0: # goal_ is a root + refL = d2roots[blocked_, goal_] + else: + refL = np.hypot(*(goalC - VertexC[blocked_]).T) + warn(f'refL: {refL:.0f}') + + is_blocked_a_clone = blocked >= N + if is_blocked_a_clone: + blockedHopI = detourHop.index(blocked) + warn(f'detourHop: {n2s(*detourHop)}') + + # TODO: this would be a good place to handle this special case + # but it requires major refactoring of the code + # if blocked_ in (u, v) and goal_ < 0 and detourHop[-2] >= N: + if False and blocked_ in (u, v) and goal_ < 0 and detourHop[-2] >= N: + # <(u, v) edge is actually pushing blocked to one of the limits of + # Barrier, this means the actual blocked hop is further away> + blockedHopI -= 1 + actual_blocked = detourHop[blockedHopI] + remove = remove | {blocked} + refL += np.hypot(*(VertexC[fnT[actual_blocked]] - + VertexC[blocked_])) + blocked = actual_blocked + blocked_ = fnT[blocked] + is_blocked_a_clone = blocked >= N + + not2hook = remove.copy() + + Barrier = list(Subtree[u] | Subtree[v]) + + store = [] + # look for detours on the Lo and Hi sides of barrier + for corner_, loNotHi, sidelabel in ((barrierLo, True, 'Lo'), + (barrierHi, False, 'Hi')): + warn(f'({depth}|{sidelabel}) BEGIN: {n2s(corner_)}') + + # block for possible future change (does nothing) + nearest_root = -M + np.argmin(d2roots[corner_]) + if nearest_root != root: + debug and print(f'[{i}] corner: {n2s(corner_)} is closest to ' + f'{n2s(nearest_root)} than to {n2s(root)}') + + # block for finding the best hook + cornerC = VertexC[corner_] + Blocked = list(Subtree[cleatnode] - remove) + if is_blocked_a_clone: + for j, (hop2check, prevhop) in enumerate( + zip(detourHop[blockedHopI::-1], + detourHop[blockedHopI - 1::-1])): + if j == 2: + print(f'[{i}] (2nd iter) depth: {depth}, ' + f'blocked: {n2s(blocked)}, ' + f'gate: {n2s(cleatnode)}') + # break + hop2check_ = fnT[hop2check] + is_hop_a_barriers_clone = hop2check_ in Barrier + prevhop_ = fnT[prevhop] + prevhopC = VertexC[prevhop_] + hop2checkC = VertexC[hop2check_] + discrim = angle(prevhopC, hop2checkC, cornerC) > 0 + is_concave_at_hop2check = ( + (not is_hop_a_barriers_clone and + (discrim != detourLoNotHi[hop2check])) or + (is_hop_a_barriers_clone and + (discrim != loNotHi))) + warn(f'concavity check at {n2s(hop2check)}: ' + f'{is_concave_at_hop2check}') + if is_concave_at_hop2check: + # warn(f'CONCAVE at {n2s(hop2check)}') + # f'at {n2s(hop2check)}: {is_concave_at_hop2check}, ' + # f'remove: {", ".join([F[r] for r in remove])}') + if hop2check not in remove: + if prevhop >= N and hop2check not in Barrier: + prevprevhop = detourHop[blockedHopI - j - 2] + prevprevhopC = VertexC[fnT[prevprevhop]] + prevhopSubTreeC = \ + VertexC[[h for h in Subtree[prevhop_] + if h < N]] + # TODO: the best thing would be to use here the + # same split algorithm used later + # (barrsplit) + if is_bunch_split_by_corner( + prevhopSubTreeC, cornerC, + prevhopC, prevprevhopC)[0]: + break + # get_crossings(corner_, prevhop_)): + # <detour can actually bypass the previous one> + Blocked.remove(hop2check) + not2hook |= {hop2check} + else: + break + # get the distance from every node in Blocked to corner + D2corner = np.squeeze(cdist(VertexC[fnT[Blocked]], + VertexC[np.newaxis, corner_])) + if not D2corner.shape: + D2corner = [float(D2corner)] + hookI = np.argmin(D2corner) + hook = Blocked[hookI] + hookC = VertexC[fnT[hook]] + + # block for calculating the length of the path to replace + prevL = refL + shift = hook != blocked + if shift and is_blocked_a_clone: + prevhop_ = blocked_ + for hop in detourHop[blockedHopI - 1::-1]: + hop_ = fnT[hop] + prevL += np.hypot(*(VertexC[hop_] - + VertexC[prevhop_])) + warn(f'adding {n2s(hop_, prevhop_)}') + prevhop_ = hop_ + if hop == hook or hop < N: + break + warn(f'prevL: {prevL:.0f}') + + # check if the bend at corner is necessary + discrim = angle(hookC, cornerC, goalC) > 0 + dropcorner = discrim != loNotHi + # if hook < N and dropcorner: + # if dropcorner and False: # TODO: conclude this test + if dropcorner and fnT[hook] != corner_: + warn(f'DROPCORNER {sidelabel}') + # <bend unnecessary> + detourL = np.hypot(*(goalC - hookC)) + addedL = prevL - detourL + # debug and print(f'[{i}] CONCAVE: ' + # print(f'[{i}] CONCAVE: ' + # f'{n2s(hook, corner_, goal_)}') + detourX = get_crossings(goal_, hook, detour_waiver=True) + if not detourX: + path = (hook,) + LoNotHi = tuple() + direct = True + store.append((addedL, path, LoNotHi, direct, shift)) + continue + warn(f'hook: {n2s(hook)}') + nearL = (d2roots[corner_, goal_] if goal_ < 0 + else np.hypot(*(goalC - cornerC))) + farL = D2corner[hookI] + addedL = farL + nearL - prevL + warn(f'{n2s(hook, corner_, goal_)} ' + f'addedL: {addedL:.0f}') + if addedL > savings: + # <detour is more costly than the savings from (u, v)> + store.append((np.inf, (hook, corner_))) + continue + + # TODO: the line below is risky. it disconsiders detour nodes + # when checking if a subtree is split + BarrierPrime = np.array([b for b in Barrier if b < N]) + BarrierC = VertexC[fnT[BarrierPrime]] + + is_barrier_split, insideI, outsideI = is_bunch_split_by_corner( + BarrierC, hookC, cornerC, goalC) + + # TODO: think if this gate edge waiver is correct + FarX = [farX for farX in get_crossings(hook, corner_, + detour_waiver=True) + if farX[0] >= 0 and farX[1] >= 0] + # this will condense multiple edges from the same subtree into one + FarXsubtree = {gnT[s]: (s, t) for s, t in FarX} + + # BEGIN barrHack block + Nin, Nout = len(insideI), len(outsideI) + if is_barrier_split and (Nin == 1 or Nout == 1): + # <possibility of doing the barrHack> + barrHackI = outsideI[0] if Nout <= Nin else insideI[0] + barrierX_ = BarrierPrime[barrHackI] + # TODO: these criteria are too ad hoc, improve it + if gnT[barrierX_] in FarXsubtree: + del FarXsubtree[gnT[barrierX_]] + elif (barrierX_ not in G[corner_] and + d2roots[barrierX_, root] > + 1.1 * d2roots[fnT[hook], root]): + # <this is a spurious barrier split> + # ignore this barrier split + is_barrier_split = False + warn('spurious barrier split detected') + else: + barrHackI = None + # END barrHack block + + # possible check to include: (something like this) + # if (anglesRank[corner_, root] > + # anglesRank[ComponHiLim[gnT[blocked], root]] + warn(f'barrsplit: {is_barrier_split}, inside: {len(insideI)}, ' + f'outside: {len(outsideI)}, total: {len(BarrierC)}') + # if is_barrier_split or get_crossings(corner_, goal_): + barrAddedL = 0 + nearX = get_crossings(corner_, goal_, detour_waiver=True) + if nearX: + warn(f'nearX: {", ".join(n2s(*X) for X in nearX)}') + if nearX or (is_barrier_split and barrHackI is None): + # <barrier very split or closer segment crosses some edge> + store.append((np.inf, (hook, corner_))) + continue + # elif (is_barrier_split and len(outsideI) == 1 and + # len(insideI) > 3): + elif is_barrier_split: + # <barrier slightly split> + # go around small interferences with the barrier itself + warn(f'SPLIT: {n2s(hook, corner_)} leaves ' + f'{n2s(BarrierPrime[barrHackI])} isolated') + # will the FarX code handle this case? + barrierXC = BarrierC[barrHackI] + # barrpath = (hook, barrierX, corner_) + # two cases: barrHop before or after corner_ + corner1st = (d2rootsRank[barrierX_, root] < + d2rootsRank[corner_, root]) + if corner1st: + barrAddedL = (np.hypot(*(goalC - barrierXC)) + + np.hypot(*(cornerC - barrierXC)) - + nearL) + barrhop = (barrierX_,) + else: + barrAddedL = (np.hypot(*(hookC - barrierXC)) + + np.hypot(*(cornerC - barrierXC)) - + farL) + barrhop = (corner_,) + corner_ = barrierX_ + warn(f'barrAddedL: {barrAddedL:.0f}') + addedL += barrAddedL + barrLoNotHi = (loNotHi,) + else: + barrhop = tuple() + barrLoNotHi = tuple() + + if len(FarXsubtree) > 1: + warn(f'NOT IMPLEMENTED: many ({len(FarXsubtree)}) ' + f'crossings of {n2s(hook, corner_)} (' + f'{", ".join([n2s(a, b) for a, b in FarXsubtree.values()])})') + store.append((np.inf, (hook, corner_))) + continue + elif FarXsubtree: # there is one crossing + subbarrier, farX = FarXsubtree.popitem() + # print('farX:', n2s(*farX)) + if depth > maxDepth: + print(f'<plan_detour[{depth}]> max depth ({maxDepth})' + 'exceeded.') + store.append((np.inf, (hook, corner_))) + continue + else: + new_barrierLo = ComponLoLim[subbarrier, root] + new_barrierHi = ComponHiLim[subbarrier, root] + remaining_savings = savings - addedL + subdetour = plan_detour( + root, hook, corner_, *farX, new_barrierLo, + new_barrierHi, remaining_savings, depth + 1, + remove=not2hook) + if subdetour is None: + store.append((np.inf, (hook, corner_))) + continue + subpath, subaddedL, subLoNotHi, subshift = subdetour + + subcorner = subpath[-1] + # TODO: investigate why plan_detour is suggesting + # hops that are not primes + subcorner_ = fnT[subcorner] + subcornerC = VertexC[subcorner_] + # check if the bend at corner is necessary + nexthopC = fnT[barrhop[0]] if barrhop else goalC + discrim = angle(subcornerC, cornerC, nexthopC) > 0 + dropcorner = discrim != loNotHi + # TODO: come back to this False + if False and dropcorner: + subcornerC = VertexC[subcorner_] + dropL = np.hypot(*(nexthopC - subcornerC)) + dc_addedL = dropL - prevL + barrAddedL + direct = len(subpath) == 1 + if not direct: + subfarL = np.hypot(*(cornerC - subcornerC)) + subnearL = subaddedL - subfarL + nearL + dc_addedL += subnearL + # debug and print(f'[{i}] CONCAVE: ' + # print(f'[{i}] CONCAVE: ' + # f'{n2s(hook, corner_, goal_)}') + dcX = get_crossings(subcorner_, corner_, + detour_waiver=True) + if not dcX: + print(f'[{i}, {depth}] dropped corner ' + f'{n2s(corner_)}') + path = (*subpath, *barrhop) + LoNotHi = (*subLoNotHi, *barrLoNotHi) + store.append((dc_addedL, path, LoNotHi, + direct, shift)) + continue + + # combine the nested detours + path = (*subpath, corner_, *barrhop) + LoNotHi = (*subLoNotHi, *barrLoNotHi) + addedL += subaddedL + shift = subshift + else: # there are no crossings + path = (hook, corner_, *barrhop) + LoNotHi = (*barrLoNotHi,) + warn(f'{sidelabel} STORE: {n2s(*path)}' + f' addedL: {addedL:.0f}') + # TODO: properly check for direct connection + # TODO: if shift: test if there is a direct path + # from hook to root + direct = False + # TODO: deprecate shift? + store.append((addedL, path, LoNotHi, direct, shift)) + + # choose between the low or high corners + if store[0][0] < savings or store[1][0] < savings: + loNotHi = store[0][0] < store[1][0] + cost, path, LoNotHi, direct, shift = store[not loNotHi] + warn(f'({depth}) ' + f'take: {n2s(*store[not loNotHi][1], goal_)} (@{cost:.0f}), ' + f'drop: {n2s(*store[loNotHi][1], goal_)} ' + f'(@{store[loNotHi][0]:.0f})') + debug and print(f'<plan_detour[{depth}]>: {n2s(u, v)} crosses ' + f'{n2s(blocked, goal_)} but {n2s(*path, goal_)} ' + 'may be used instead.') + return (path, cost, LoNotHi + (loNotHi,), shift) + return None + + def add_corner(hook, corner_, cleatnode, loNotHi): + nonlocal D + D += 1 + + if D > Dmax: + # TODO: extend VertexC, fnT and gnT + print('@@@@@@@@@@@@@@ Dmax REACHED @@@@@@@@@@@@@@') + corner = N + D - 1 + + # update coordinates mapping fnT + fnT[corner] = corner_ + + # update gates mapping gnT + # subtree being rerouted + gnT[corner] = cleatnode + Subtree[cleatnode].add(corner) + Subtree[corner] = Subtree[cleatnode] + + # update DetourHop + DetourHop[cleatnode].append(corner) + # update detourLoNotHi + detourLoNotHi[corner] = loNotHi + # add Detour node + G.add_node(corner, type='detour', root=root) + log.append((i, 'addDN', (corner_, corner))) + # add detour edges + length = np.hypot(*(VertexC[fnT[hook]] - + VertexC[corner_]).T) + G.add_edge(hook, corner, length=length, + type='detour', color='yellow', style='dashed') + log.append((i, 'addDE', (hook, corner, fnT[hook], corner_))) + return corner + + def move_corner(corner, hook, corner_, cleatnode, loNotHi): + # update translation tables + fnT[corner] = corner_ + + # update DetourHop + DetourHop[cleatnode].append(corner) + # update detourLoNotHi + detourLoNotHi[corner] = loNotHi + # update edges lengths + farL = np.hypot(*(VertexC[fnT[hook]] - + VertexC[corner_]).T) + # print(f'[{i}] updating {n2s(hook, corner)}') + G[hook][corner].update(length=farL) + log.append((i, 'movDN', (hook, corner, fnT[hook], corner_))) + return corner + + def make_detour(blocked, path, LoNotHi, shift): + hook, *Corner_ = path + + cleatnode = gnT[blocked] + root = G.nodes[cleatnode]['root'] + # if Corner_[0] is None: + if not Corner_: + # <a direct gate replacing previous gate> + # TODO: this case is very outdated, probably wrong + debug and print(f'[{i}] <make_detour> direct gate ' + f'{n2s(hook, root)}') + # remove previous gate + Final_G[root].remove(blocked) + Subtree[cleatnode].remove(blocked) + G.remove_edge(blocked, root) + log.append((i, 'remE', (blocked, root))) + # make a new direct gate + length = d2roots[fnT[hook], root] + G.add_edge(hook, root, length=length, + type='detour', color='yellow', style='dashed') + log.append((i, 'addDE', (hook, root, fnT[hook], root))) + Final_G[root].add(hook) + else: + detourHop = DetourHop[cleatnode] + if blocked < N or hook == blocked: + # <detour only affects the blocked gate edge> + + # remove the blocked gate edge + Final_G[root].remove(blocked) + G.remove_edge(blocked, root) + log.append((i, 'remE', (blocked, root))) + + # create new corner nodes + if hook < N: + # add the first entry in DetourHop (always prime) + detourHop.append(hook) + for corner_, loNotHi in zip(Corner_, LoNotHi): + corner = add_corner(hook, corner_, cleatnode, loNotHi) + hook = corner + # add the gate edge from the last corner node created + length = d2roots[corner_, root] + G.add_edge(corner, root, length=length, + type='detour', color='yellow', style='dashed') + log.append((i, 'addDE', (corner, root, corner_, root))) + Final_G[root].add(corner) + else: + # <detour affects edges further from blocked node> + + assert blocked == detourHop[-1] + # stales = iter(detourHop[-2:0:-1]) + + # number of new corners needed + newN = len(Corner_) - len(detourHop) + 1 + + try: + j = detourHop.index(hook) + except ValueError: + # <the path is starting from a new prime> + j = 0 + stales = iter(detourHop[1:]) + k = abs(newN) if newN < 0 else 0 + new2stale_cut = detourHop[k:k + 2] + detourHop.clear() + detourHop.append(hook) + else: + stales = iter(detourHop[j + 1:]) + newN += j + k = j + (abs(newN) if newN < 0 else 0) + new2stale_cut = detourHop[k:k + 2] + del detourHop[j + 1:] + # print(f'[{i}] <make_detour> removing {n2s(*new2stale_cut)}, ' + # f'{new2stale_cut in G.edges}') + # newN += j + # TODO: this is not handling the case of more stale hops than + # necessary for the detour path (must at least cleanup G) + if newN < 0: + print(f'[{i}] WARNING <make_detour> more stales than ' + f'needed: {abs(newN)}') + while newN < 0: + stale = next(stales) + G.remove_node(stale) + log.append((i, 'remN', stale)) + Subtree[cleatnode].remove(stale) + Stale.append(stale) + newN += 1 + else: + G.remove_edge(*new2stale_cut) + log.append((i, 'remE', new2stale_cut)) + for j, (corner_, loNotHi) in enumerate(zip(Corner_, LoNotHi)): + if j < newN: + # create new corner nodes + corner = add_corner(hook, corner_, cleatnode, loNotHi) + else: + stale = next(stales) + if j == newN: + # add new2stale_cut edge + # print(f'[{i}] adding {n2s(hook, stale)}') + G.add_edge(hook, stale, type='detour', + color='yellow', style='dashed') + log.append((i, 'addDE', (hook, stale, + fnT[hook], corner_))) + # move the stale corners to their new places + corner = move_corner(stale, hook, corner_, + cleatnode, loNotHi) + hook = corner + # update the gate edge length + nearL = d2roots[corner_, root] + G[corner][root].update(length=nearL) + log.append((i, 'movDN', (corner, root, corner_, root))) + + def check_gate_crossings(u, v, g2keep, g2drop): + nonlocal tradeoff + + union = list(Subtree[u] | Subtree[v]) + r2keep = G.nodes[g2keep]['root'] + r2drop = G.nodes[g2drop]['root'] + + if r2keep == r2drop: + roots2check = (r2keep,) + else: + roots2check = (r2keep, r2drop) + + # assess the union's angle span + unionHi = np.empty((len(roots),), dtype=int) + unionLo = np.empty((len(roots),), dtype=int) + for root in roots: + keepHi = ComponHiLim[g2keep, root] + keepLo = ComponLoLim[g2keep, root] + dropHi = ComponHiLim[g2drop, root] + dropLo = ComponLoLim[g2drop, root] + unionHi[root] = ( + dropHi if angle(*VertexC[fnT[[keepHi, root, dropHi]]]) > 0 + else keepHi) + unionLo[root] = ( + dropLo if angle(*VertexC[fnT[[dropLo, root, keepLo]]]) > 0 + else keepLo) + # debug and print(f'<angle_span> //{F[unionLo]} : ' + # f'{F[unionHi]}//') + + abort = False + Detour = {} + + for root in roots2check: + for g2check in Final_G[root] - {v}: + if (is_crossing_gate(root, g2check, u, v, + touch_is_cross=True) or + (g2check >= N and fnT[g2check] in (u, v) and + (is_bunch_split_by_corner( + VertexC[fnT[union]], + *VertexC[fnT[[DetourHop[gnT[g2check]][-2], + g2check, root]]])[0]))): + # print('getting detour') + # detour = plan_detour(root, g2check, + # u, v, unionLo[root], + # unionHi[root], -tradeoff) + # TODO: it would be worth checking if changing roots is the + # shortest path to avoid the (u, v) block + detour = plan_detour( + root, g2check, root, u, v, + unionLo[root], unionHi[root], -tradeoff) + if detour is not None: + Detour[g2check] = detour + else: + debug and print(f'<check_gate_crossings> discarding ' + f'{n2s(u, v)}: ' + f'would block gate {n2s(g2check)}') + abort = True + break + if abort: + break + + if not abort and Detour: + debug and print( + f'<check_gate_crossings> detour options: ' + f'{", ".join(n2s(*path) for path, _, _, _ in Detour.values())}') + # <crossing detected but detours are possible> + detoursCost = sum((cost for _, cost, _, _ in Detour.values())) + if detoursCost < -tradeoff: + # add detours to G + detdesc = [f'blocked {n2s(blocked)}, ' + f'gate {n2s(gnT[blocked])}, ' + f'{n2s(*path)} ' + f'@{cost:.0f}' + for blocked, (path, cost, loNotHi, shift) + in Detour.items()] + warn('\n' + '\n'.join(detdesc)) + for blocked, (path, _, LoNotHi, shift) in Detour.items(): + make_detour(blocked, path, LoNotHi, shift) + else: + debug and print( + f'Multiple Detour cancelled for {n2s(u, v)} ' + f'(tradeoff gain = {-tradeoff:.0f}) × ' + f'(cost = {detoursCost:.0f}):\n' # + + # '\n'.join(detourdesc)) + ) + abort = True + return abort, unionLo, unionHi + + # initialize pq + for n in range(N): + find_option4gate(n) + # create a global tradeoff variable + tradeoff = 0 + + # BEGIN: main loop + def loop(): + '''Takes a step in the iterative tree building process. + Return value [bool]: not done.''' + nonlocal i, prevented_crossings, tradeoff + while True: + i += 1 + if i > maxiter: + print(f'ERROR: maxiter reached ({i})') + return + if gates2upd8: + debug and print('<loop> gates2upd8:', + ', '.join(F[gate] for gate in gates2upd8)) + while gates2upd8: + find_option4gate(gates2upd8.pop()) + if not pq: + # finished + return + tradeoff = pq[0][0] + debug and print(f'\n[{i}] tradeoff gain = {-tradeoff:.0f}') + g2drop, (u, v) = pq.top() + debug and print(f'<loop> POPPED {n2s(u, v)},' + f' g2drop: <{F[g2drop]}>') + capacity_left = capacity - len(Subtree[u]) - len(Subtree[v]) + + if capacity_left < 0: + print('@@@@@ Does this ever happen?? @@@@@') + ban_queued_edge(g2drop, u, v) + yield (u, v), False + continue + + # BEGIN edge crossing check + # check if (u, v) crosses an existing edge + # look for crossing edges within the neighborhood of (u, v) + # only works if using the expanded delaunay edges + # eX = edge_crossings(u, v, G, triangles, triangles_exp) + eX = edge_crossings(u, v, G, diagonals, P) + # Detour edges need to be checked separately + if not eX and D: + uC, vC = VertexC[fnT[[u, v]]] + eXtmp = [] + eXnodes = set() + nodes2check = set() + BarrierC = VertexC[fnT[list(Subtree[u] | Subtree[v])]] + for s, t in G.edges(range(N, N + D)): + skip = False + if s < 0 or t < 0: + # skip gates (will be checked later) + continue + s_, t_ = fnT[[s, t]] + Corner = [] + # below are the 2 cases in which a new edge + # will join two subtrees across a detour edge + if (s >= N and (s_ == u or s_ == v) and + s != DetourHop[gnT[s]][-1]): + Corner.append(s) + if (t >= N and (t_ == u or t_ == v) and + t != DetourHop[gnT[t]][-1]): + Corner.append(t) + for corner in Corner: + a, b = G[corner] + if is_bunch_split_by_corner( + BarrierC, + *VertexC[fnT[[a, corner, b]]])[0]: + debug and print(f'[{i}] {n2s(u, v)} ' + 'would cross ' + f'{n2s(a, corner, b)}') + eX.append((a, corner, b)) + skip = True + if skip: + continue + if is_crossing(uC, vC, *VertexC[fnT[[s, t]]], + touch_is_cross=False): + eXtmp.append((s, t)) + if s in eXnodes: + nodes2check.add(s) + if t in eXnodes: + nodes2check.add(t) + eXnodes.add(s) + eXnodes.add(t) + for s, t in eXtmp: + if s in nodes2check: + for w in G[s]: + if w != t and not is_same_side( + uC, vC, *VertexC[fnT[[w, t]]]): + eX.append((s, t)) + elif t in nodes2check: + for w in G[t]: + if w != s and not is_same_side( + uC, vC, *VertexC[fnT[[w, s]]]): + eX.append((s, t)) + else: + eX.append((s, t)) + + if eX: + debug and print(f'<edge_crossing> discarding {n2s(u, v)} – would ' + f'cross: {", ".join(n2s(s, t) for s, t in eX)}') + # abort_edge_addition(g2drop, u, v) + prevented_crossings += 1 + ban_queued_edge(g2drop, u, v) + yield (u, v), None + continue + # END edge crossing check + + # BEGIN gate crossing check + # check if (u, v) crosses an existing gate + g2keep = gnT[v] + root = G.nodes[g2keep]['root'] + r2drop = G.nodes[g2drop]['root'] + if root != r2drop: + debug and print(f'<distinct_roots>: [{F[u]}] is bound to ' + f'[{F[r2drop]}], while' + f'[{F[v]}] is to {F[root]}.') + + abort, unionLo, unionHi = check_gate_crossings(u, v, g2keep, + g2drop) + if abort: + prevented_crossings += 1 + ban_queued_edge(g2drop, u, v) + yield (u, v), None + continue + # END gate crossing check + + # (u, v) edge addition starts here + subtree = Subtree[v] + subtree |= Subtree[u] + G.remove_edge(A.nodes[u]['root'], g2drop) + log.append((i, 'remE', (A.nodes[u]['root'], g2drop))) + + g2keep_entry = pq.tags.get(g2keep) + if g2keep_entry is not None: + _, _, _, (_, t) = g2keep_entry + # print('node', F[t], 'gate', F[gnT[t]]) + ComponIn[gnT[t]].remove(g2keep) + # TODO: think about why a discard was needed + ComponIn[g2keep].discard(g2drop) + + # update the component's angle span + ComponHiLim[g2keep] = unionHi + ComponLoLim[g2keep] = unionLo + + # assign root, gate and subtree to the newly added nodes + for n in Subtree[u]: + A.nodes[n]['root'] = root + G.nodes[n]['root'] = root + gnT[n] = g2keep + Subtree[n] = subtree + debug and print(f'<loop> NEW EDGE {n2s(u, v)}, g2keep ' + f'<{F[g2keep]}>, ' if pq else 'EMPTY heap') + # G.add_edge(u, v, **A.edges[u, v]) + G.add_edge(u, v, length=A[u][v]['length']) + log.append((i, 'addE', (u, v))) + # remove from consideration edges internal to Subtree + A.remove_edge(u, v) + + # finished adding the edge, now check the consequences + if capacity_left > 0: + for gate in list(ComponIn[g2keep]): + if len(Subtree[gate]) > capacity_left: + # <this subtree got too big for Subtree[gate] to join> + ComponIn[g2keep].discard(gate) + gates2upd8.add(gate) + # for gate in ComponIn[g2drop]: + for gate in ComponIn[g2drop] - ComponIn[g2keep]: + if len(Subtree[gate]) > capacity_left: + gates2upd8.add(gate) + else: + ComponIn[g2keep].add(gate) + gates2upd8.add(g2keep) + else: + # max capacity reached: subtree full + if g2keep in pq.tags: # required because of i=0 gates + pq.cancel(g2keep) + make_gate_final(root, g2keep) + # don't consider connecting to this full subtree anymore + A.remove_nodes_from(subtree) + # for gate in ((ComponIn[g2drop] | ComponIn[g2keep]) - {g2drop, + # g2keep}): + for gate in ComponIn[g2drop] | ComponIn[g2keep]: + gates2upd8.add(gate) + # END: main loop + + log = [] + G.graph['log'] = log + for result in loop(): + pass + + if Stale: + debug and print(f'Stale nodes ({len(Stale)}):', [n2s(n) for n in Stale]) + old2new = np.arange(N, N + D) + mask = np.ones(D, dtype=bool) + for s in Stale: + old2new[s - N + 1:] -= 1 + mask[s - N] = False + mapping = dict(zip(range(N, N + D), old2new)) + for k in Stale: + mapping.pop(k) + nx.relabel_nodes(G, mapping, copy=False) + fnT[N:N + D - len(Stale)] = fnT[N:N + D][mask] + D -= len(Stale) + + debug and print(f'FINISHED – Detour nodes added: {D}') + + # G.graph['DetourC'] = VertexC[N:N + D].copy() + G.graph['D'] = D + # G.graph['clone2prime'] = fnT[N:N + D].copy() + G.graph['fnT'] = np.concatenate((fnT[:N + D], fnT[-M:])) + + if debug: + not_marked = [] + for root in roots: + for gate in G[root]: + if gate not in Final_G[root]: + not_marked.append(gate) + not_marked and print('@@@@ WARNING: gates ' + f'<{", ".join([F[gate] for gate in not_marked])}' + '> were not marked as final @@@@') + + # algorithm finished, store some info in the graph object + G.graph['iterations'] = i + G.graph['prevented_crossings'] = prevented_crossings + G.graph['capacity'] = capacity + G.graph['overfed'] = [len(G[root]) / np.ceil(N / capacity) * M + for root in roots] + G.graph['edges_created_by'] = 'OBEW' + G.graph['edges_fun'] = OBEW + G.graph['creation_options'] = options + G.graph['runtime_unit'] = 's' + G.graph['runtime'] = time.perf_counter() - start_time + return G diff --git a/ed_win/plotting_scripts/__init__.py b/ed_win/plotting_scripts/__init__.py new file mode 100644 index 0000000..0de16d3 --- /dev/null +++ b/ed_win/plotting_scripts/__init__.py @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +# author, version, license, and long description +__version__ = '0.0.1' +__author__ = 'Mauricio Souza de Alencar' + +# global module constants +MAX_TRIANGLE_ASPECT_RATIO = 15.0 diff --git a/ed_win/plotting_scripts/augmentation.py b/ed_win/plotting_scripts/augmentation.py new file mode 100644 index 0000000..ed30453 --- /dev/null +++ b/ed_win/plotting_scripts/augmentation.py @@ -0,0 +1,351 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +from typing import Optional, Tuple, Callable +import matplotlib.pyplot as plt +from matplotlib.ticker import MultipleLocator +from .utils import NodeTagger + +import shapely as shp +import numpy as np +import numba as nb +import networkx as nx + +F = NodeTagger() + + +# iCDF_factory(N_min = 70, N_max = 200, η = 0.6, d_lb = 0.045): +def iCDF_factory(N_min: int, N_max: int, η: float, d_lb: float)\ + -> Callable[[float], int]: + ''' + Create function to shape the PDF: y(x) = d(N) - d_lb = 2*sqrt(η/π/N) - d_lb + where: + N is the WT count + η is the area covered by N circles of diameter d (η = Nπd²/4) + d_lb is the lower bound for the minimum distance between WT + ''' + + def integral(x): # integral of y(x) wrt x + return 4 * np.sqrt(x * η / np.pi) - d_lb * x + + def integral_inv(y): # integral_inv(integral(x)) = x + return ((-4 * np.sqrt(4 * η**2 - np.pi * η * d_lb * y) + 8 * η - np.pi * d_lb * y) / + (np.pi * d_lb**2)) + + offset = integral(N_min - 0.4999999) + area_under_curve = integral(N_max + 0.5) - offset + + def iCDF(u: float) -> int: + '''Map from u ~ uniform(0, 1) to random variable N ~ custom \ + probability density function''' + return int(round(integral_inv(u * area_under_curve + offset))) + + return iCDF + + +def normalize_site_single_oss(G: nx.Graph)\ + -> Tuple[float, np.ndarray, np.ndarray, np.ndarray]: + ''' + Calculate the area and scale the boundary so that it has area 1. + The boundary and OSS are translated to the 1st quadrant, near the origin. + + IF SITE HAS MULTIPLE OSSs, ONLY 1 IS RETURNED (mean of the OSSs' coords). + ''' + BoundaryC = G.graph['boundary'] + bound_poly = shp.Polygon(BoundaryC) + corner_lo, corner_hi = tuple(np.array(bound_poly.bounds[A:B]) + for A, B in ((0, 2), (2, 4))) + M = G.graph['M'] + VertexC = G.graph['VertexC'] + factor = 1 / np.sqrt(bound_poly.area) + BoundaryC -= corner_lo + BoundaryC *= factor + oss = ((VertexC[-M:].mean(axis=0) - corner_lo) * factor)[np.newaxis, :] + return factor, corner_lo, BoundaryC, oss, (corner_hi - corner_lo) * factor + + +def build_instance_graph(WTpos, boundary, name='', handle='unnamed', oss=None, + landscape_angle=0): + N = WTpos.shape[0] + if oss is not None: + M = oss.shape[0] + VertexC = np.concatenate((WTpos, oss)) + else: + M = 0 + VertexC = WTpos + G = nx.Graph( + name=name, + handle=handle, + M=M, + boundary=boundary, + landscape_angle=landscape_angle, + VertexC=VertexC) + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + return G + + +@nb.njit(cache=True, inline='always') +def clears(repellers: nb.float64[:, :], clearance_sq: np.float64, + point: nb.float64[:]) -> bool: + ''' + Evaluate if `point` (2,) is more that sqrt(clearance_sq) away from all + `repellers` (K×2). + Return boolean. + ''' + return (((point[np.newaxis, :] - repellers)**2).sum(axis=1) >= + clearance_sq).all() + + +def contains_np(polygon: nb.float64[:, :], + pts: nb.float64[:, :]) -> nb.bool_[:]: + ''' + Evaluate if 2D points in `pts` (N×2) are inside `polygon` (K×2, CCW vertex + order). + Return 1D boolean array (True if pts[i] inside `polygon`). + ''' + polygon_rolled = np.roll(polygon, -1, axis=0) + vectors = polygon_rolled - polygon + mask1 = (pts[:, None] == polygon).all(-1).any(-1) + m1 = ((polygon[:, 1] > pts[:, None, 1]) != + (polygon_rolled[:, 1] > pts[:, None, 1])) + slope = ((pts[:, None, 0] - polygon[:, 0]) * vectors[:, 1]) - ( + vectors[:, 0] * (pts[:, None, 1] - polygon[:, 1])) + m2 = slope == 0 + mask2 = (m1 & m2).any(-1) + m3 = (slope < 0) != (polygon_rolled[:, 1] < polygon[:, 1]) + m4 = m1 & m3 + count = np.count_nonzero(m4, axis=-1) + mask3 = ~(count % 2 == 0) + return mask1 | mask2 | mask3 + + +@nb.njit(cache=True) +def contains(polygon: nb.float64[:, :], point: nb.float64[:]) -> bool: + ''' + Evaluate if `point` (2,) is inside `polygon` (K×2, CCW vertex + order). + Return boolean. + ''' + intersections = 0 + dx2, dy2 = point - polygon[-1] + + for p in polygon: + dx, dy = dx2, dy2 + dx2, dy2 = point - p + + F = (dx - dx2) * dy - dx * (dy - dy2) + if np.isclose(F, 0., rtol=0.) and dx * dx2 <= 0 and dy * dy2 <= 0: + return True + + if (dy >= 0 and dy2 < 0) or (dy2 >= 0 and dy < 0): + if F > 0: + intersections += 1 + elif F < 0: + intersections -= 1 + return intersections != 0 + + +def poisson_disc_filler(N: int, min_dist: float, boundary: nb.float64[:, :], + repellers: Optional[nb.float64[:, :]] = None, + clearance: float = 0, exclude=None, seed=None, + iter_max_factor: int = 50, plot: bool = False, + partial_fulfilment: bool = True) -> nb.float64[:, :]: + ''' + Fills the area delimited by `boundary` with `N` randomly + placed points that are at least `min_dist` apart and that + don't fall inside any of the `repellers` discs or `exclude` areas. + :param N: + :param min_dist: + :param boundary: iterable (B × 2) with CCW-ordered vertices of a polygon + :param repellers: iterable (M × 2) with the centers of forbidden discs + :param clearance: the radius of the forbidden discs + :param exclude: iterable (E × X × 2) + :param iter_max_factor: factor to multiply by `N` to limit the number of + iterations + :param partial_fulfilment: whether to return less than `N` points (True) or + to raise exception (False) if unable to fulfill + request. + :return numpy array shaped (N, 2) with points' positions + ''' + # TODO: implement exclusion zones + if exclude is not None: + raise NotImplementedError + + bound_poly = shp.Polygon(boundary) + area_avail = bound_poly.area + corner_lo, corner_hi = tuple(np.array(bound_poly.bounds[A:B]) + for A, B in ((0, 2), (2, 4))) + + # quick check for outrageous densities + # circle packing efficiency limit: η = π srqt(3)/6 = 0.9069 + # A Simple Proof of Thue's Theorem on Circle Packing + # https://arxiv.org/abs/1009.4322 + area_demand = N * np.pi * min_dist**2 / 4 + efficiency = area_demand / area_avail + efficiency_optimal = np.pi * np.sqrt(3) / 6 + if efficiency > efficiency_optimal: + msg = (f"(N = {N}, min_dist = {min_dist}) imply a packing " + f"efficiency of {efficiency:.3f} which is higher than " + f"the optimal possible ({efficiency_optimal:.3f}).") + if partial_fulfilment: + print('Info: Attempting partial fullfillment.', msg, + 'Try with lower N and/or min_dist.') + else: + raise ValueError(msg) + + # create auxiliary grid covering the defined boundary + cell_size = min_dist / np.sqrt(2) + i_len, j_len = np.ceil( + (corner_hi - corner_lo) / cell_size + ).astype(np.int_) + boundary_scaled = (boundary - corner_lo) / cell_size + if repellers is None: + repellers_scaled = None + clearance_sq = 0. + else: + repellers_scaled = (repellers - corner_lo) / cell_size + clearance_sq = (clearance / cell_size)**2 + + # Alternate implementation using np.mgrid + # pts = np.reshape( + # np.moveaxis(np.mgrid[0: i_len + 1, 0: j_len + 1], 0, -1), + # ((i_len + 1)*(j_len + 1), 2) + # ) + pts = np.empty(((i_len + 1) * (j_len + 1), 2), dtype=int) + pts_temp = pts.reshape((i_len + 1, j_len + 1, 2)) + pts_temp[..., 0] = np.arange(i_len + 1)[:, np.newaxis] + pts_temp[..., 1] = np.arange(j_len + 1)[np.newaxis, :] + inside = contains_np(boundary_scaled, pts).reshape((i_len + 1, j_len + 1)) + + # reduce 2×2 sub-matrices of `inside` with logical_or (i.e. .any()) + cell_covers_polygon = np.lib.stride_tricks.as_strided( + inside, shape=(2, 2, inside.shape[0] - 1, inside.shape[1] - 1), + strides=inside.strides * 2, writeable=False + ).any(axis=(0, 1)) + + # check boundary's vertices + for k, (i, j) in enumerate(boundary_scaled.astype(int)): + if not cell_covers_polygon[i, j]: + ij = boundary_scaled[k].copy() + direction = boundary_scaled[k - 1] - ij + direction /= np.linalg.norm(direction) + to_mark = [(i, j)] + while True: + nbr = (cell_covers_polygon[max(0, i - 1), j] or + cell_covers_polygon[min(i_len - 1, i + 1), j] or + cell_covers_polygon[i, max(0, j - 1)] or + cell_covers_polygon[i, min(j_len - 1, j + 1)]) + if nbr: + break + ij += direction * 0.999 + i, j = ij.astype(int) + to_mark.append((i, j)) + for i, j in to_mark: + cell_covers_polygon[i, j] = True + + # Sequence of (i, j) of cells that overlap with the polygon + cell_idc = np.argwhere(cell_covers_polygon) + + iter_max = iter_max_factor * N + rng = np.random.default_rng(seed) + + # useful plot for debugging purposes only + if plot: + fig, ax = plt.subplots() + ax.imshow(cell_covers_polygon.T, origin='lower', + extent=[0, cell_covers_polygon.shape[0], + 0, cell_covers_polygon.shape[1]]) + ax.scatter(*np.nonzero(inside), marker='.') + ax.scatter(*boundary_scaled.T, marker='x') + ax.plot(*np.vstack((boundary_scaled, boundary_scaled[:1])).T) + ax.xaxis.set_major_locator(MultipleLocator(1)) + ax.yaxis.set_major_locator(MultipleLocator(1)) + ax.grid() + + # point-placing function + points = wrapped_poisson_disc_filler( + N, iter_max, i_len, j_len, cell_idc, boundary_scaled, clearance_sq, + repellers_scaled, rng) + + # check if request was fulfilled + if len(points) < N: + msg = (f'Only {len(points)} points generated (requested: {N}, itera' + f'tions: {iter_max}, efficiency requested: {efficiency:.3f}, ' + f'efficiency limit: {efficiency_optimal:.3f})') + print('WARNING:', msg) + + return points * cell_size + corner_lo + + +@nb.njit(cache=True) +def wrapped_poisson_disc_filler( + N: int, iter_max: int, i_len: int, j_len: int, + cell_idc: nb.int64[:, :], boundary_scaled: nb.float64[:, :], + clearance_sq: float, repellers_scaled: Optional[nb.float64[:, :]], + rng: np.random.Generator) -> nb.float64[:, :]: + '''See `poisson_disc_filler()`.''' + # [Poisson-Disc Sampling](https://www.jasondavies.com/poisson-disc/) + + # mask for the 20 neighbors + # (5x5 grid excluding corners and center) + neighbormask = np.array(((False, True, True, True, False), + (True, True, True, True, True), + (True, True, False, True, True), + (True, True, True, True, True), + (False, True, True, True, False))) + + # points to be returned by this function + points = np.empty((N, 2), dtype=np.float64) + # grid for mapping of cell to position in array `points` (N means not set) + cells = np.full((i_len, j_len), N, dtype=np.int64) + + def no_conflict(p: int, q: int, point: nb.float64[:]) -> bool: + ''' + Check for conflict with points from the 20 cells neighboring the + current cell. + :param p: x cell index. + :param q: y cell index. + :param point: numpy array shaped (2,) with the point's coordinates + :return True if point does not conflict, False otherwise. + ''' + p_min, p_max = max(0, p - 2), min(i_len, p + 3) + q_min, q_max = max(0, q - 2), min(j_len, q + 3) + cells_window = cells[p_min:p_max, q_min:q_max].copy() + mask = (neighbormask[2 + p_min - p: 2 + p_max - p, + 2 + q_min - q: 2 + q_max - q] & + (cells_window < N)) + ii = cells_window.reshape(mask.size)[np.flatnonzero(mask.flat)] + return not (((point[np.newaxis, :] - points[ii])**2).sum(axis=-1) < + 2).any() + + out_count = 0 + idc_list = list(range(len(cell_idc))) + + # dart-throwing loop + for iter_count in range(1, iter_max + 1): + # pick random empty cell + empty_idx = rng.integers(low=0, high=len(idc_list)) + ij = cell_idc[idc_list[empty_idx]] + i, j = ij + + # dart throw inside cell + dartC = ij + rng.random(2) + + # check boundary, overlap and clearance + if contains(boundary_scaled, dartC): + if no_conflict(i, j, dartC): + if repellers_scaled is not None: + if not clears(repellers_scaled, clearance_sq, dartC): + continue + # add new point and remove cell from empty list + points[out_count] = dartC + cells[i, j] = out_count + del idc_list[empty_idx] + out_count += 1 + if out_count == N or not idc_list: + break + + return points[:out_count] diff --git a/ed_win/plotting_scripts/clusterlib.py b/ed_win/plotting_scripts/clusterlib.py new file mode 100644 index 0000000..b25ed6a --- /dev/null +++ b/ed_win/plotting_scripts/clusterlib.py @@ -0,0 +1,359 @@ +''' +Module to help writing scripts for programatic job submission to the cluster +(LSF-based). This module needs some polishing. +''' + +import json +import math +import os +import sys +import traceback +import subprocess + +from textwrap import dedent +import dill +from pony.orm import db_session +import numpy as np + +from .new_dbmodel import open_database +from .new_storage import packmethod, packnodes +from .pathfinding import PathFinder +from .MILP.pyomo import MILP_solution_to_G as cplex_MILP_solution_to_G +from .interarraylib import fun_fingerprint +from .geometric import make_graph_metrics + + +class DBhelper(): + ''' + This class has a single exposed method: .is_in_database() + Its purpose is to avoid running cases already stored in the database. + ''' + + def __init__(self, database): + # initialize database connection + self.db = open_database(database) + + def is_in_database(self, G, capacity, edges_fun, method_options): + # Make sure nodeset is compatible with the database. + # There have been some issues with same nodeset + # resulting in different digests and being added twice + # (program fails because nodeset.name must be unique). + nodes_pack = packnodes(G) + method_pack = packmethod(fun_fingerprint(edges_fun), method_options) + with db_session: + nodes_entry = self.db.NodeSet.get(name=nodes_pack['name']) + if nodes_entry is not None: + print(f'NodeSet found in the database: {nodes_entry.name}') + # if NodeSet name already in use, but digest is different do not + # allow going forward + if nodes_entry.digest != nodes_pack['digest']: + print('Error: nodeset name <' + nodes_pack['name'] + + '> already in use.', file=sys.stderr) + return True + + # check if there is a result for the same pair (NodeSet, Method) + with db_session: + if self.db.Method.exists(digest=method_pack['digest']): + print('Method found in the database.') + if self.db.EdgeSet.exists( + nodes=self.db.NodeSet[nodes_entry.digest], + method=self.db.Method[method_pack['digest']], + capacity=capacity): + # TODO: decide whether to allow it to run if machine is + # different. For the moment, not allowing. + print('Skipping: result is already in the database. ' + 'Exiting...', file=sys.stderr) + return True + return False + + +def from_environment(): + method_options = json.loads(os.environ['INTERARRAY_METHOD']) + problem_options = json.loads(os.environ['INTERARRAY_PROBLEM']) + database = os.environ['INTERARRAY_DATABASE'] + return method_options, problem_options, database + + +def to_environment(problem_options: dict, method_options: dict, + database: str): + os.environ['INTERARRAY_METHOD'] = json.dumps(method_options) + os.environ['INTERARRAY_PROBLEM'] = json.dumps(problem_options) + os.environ['INTERARRAY_DATABASE'] = database + return ['INTERARRAY_METHOD', 'INTERARRAY_PROBLEM', 'INTERARRAY_DATABASE'] + + +def dict_from_solver_status(solver_name, solver, status): + keys = ('bound', 'objective', 'MILPtermination', 'runtime') + if solver_name == 'gurobi': + return dict(zip(keys, [ + solver.results['Problem'][0]['Lower bound'], + solver.results['Problem'][0]['Upper bound'], + solver.results['Solver'][0]['Termination condition'].value, + solver.results['Solver'][0]['Wallclock time'], + ])) + elif solver_name == 'cplex' or solver_name == 'beta': + return dict(zip(keys, [ + status['Problem'][0]['Lower bound'], + status['Problem'][0]['Upper bound'], + status['Solver'][0]['Termination condition'], + status['Solver'][0]['Wallclock time'], + ])) + elif solver_name == 'ortools': + return dict(zip(keys, [ + solver.BestObjectiveBound(), + solver.ObjectiveValue(), + solver.StatusName(), + solver.WallTime(), + ])) + + +def cplex_load_solution_from_pool(solver, soln): + cplex = solver._solver_model + vals = cplex.solution.pool.get_values(soln) + vars_to_load = solver._pyomo_var_to_ndx_map.keys() + for pyomo_var, val in zip(vars_to_load, vals): + if solver._referenced_variables[pyomo_var] > 0: + pyomo_var.set_value(val, skip_validation=True) + + +def cplex_investigate_pool(A, G, m, solver, info2store): + '''Go through the CPLEX solutions checking which has the shortest length + after applying the detours with PathFinder.''' + # process the best layout first + H = try_pathfinding_with_exc_handling(info2store, solver, G) + H_incumbent = H + L_incumbent = H.size(weight='length') + print(f'First incumbent has length: {L_incumbent:.3f}') + # now check the additional layouts + cplex = solver._solver_model + # G was generated with the first solution in the sorted Pool: skip it + Pool = sorted((cplex.solution.pool.get_objective_value(i), i) + for i in range(cplex.solution.pool.get_num()))[1:] + print(f'Solution pool has {len(Pool)} solutions') + for L_pool, soln in Pool: + if L_incumbent < L_pool: + print('Finished analyzing solution pool.') + break + cplex_load_solution_from_pool(solver, soln) + G = cplex_MILP_solution_to_G(m, solver, A) + H = try_pathfinding_with_exc_handling(info2store, solver, G) + L_contender = H.size(weight='length') + if L_contender < L_incumbent: + L_incumbent = L_contender + H_incumbent = H + print(f'New incumbent found with length: {L_incumbent:.3f}') + return H_incumbent + + +def cplex_investigate_pool_inplace(A, G, m, solver, info2store): + '''Go through the CPLEX solutions checking which has the shortest length + after applying the detours with PathFinder.''' + # process the best layout first + try_pathfinding_with_exc_handling(info2store, solver, G, in_place=True) + G_incumbent = G + L_incumbent = G.size(weight='length') + print(f'First incumbent has length: {L_incumbent:.0f}') + # now check the additional layouts + cplex = solver._solver_model + # G was generated with the first solution in the sorted Pool: skip it + Pool = sorted((cplex.solution.pool.get_objective_value(i), i) + for i in range(cplex.solution.pool.get_num()))[1:] + print(f'Solution pool has {len(Pool)} solutions') + for L_pool, soln in Pool: + if L_incumbent < L_pool: + print('Finished analyzing solution pool.') + break + cplex_load_solution_from_pool(solver, soln) + G = cplex_MILP_solution_to_G(m, solver, A) + try_pathfinding_with_exc_handling(info2store, solver, G, in_place=True) + L_contender = G.size(weight='length') + if L_contender < L_incumbent: + L_incumbent = L_contender + G_incumbent = G + print(f'New incumbent found with length: {L_incumbent:.3f}') + return G_incumbent + + +def try_pathfinding_with_exc_handling(info2store, solver, G, in_place=False): + dumpgraphs = False + print('Instantiating PathFinder...') + try: + pf = PathFinder(G) + print('Creating detours...') + try: + H = pf.create_detours(in_place=in_place) + except Exception: + traceback.print_exc() + # print(f'Exception "{exc}" caught while creating detours.', + # file=sys.stderr) + dumpgraphs = True + partial_solution = True + except Exception: + traceback.print_exc() + # print(f'Exception "{exc}" caught while instantiating PathFinder.', + # file=sys.stderr) + dumpgraphs = True + partial_solution = False + + if dumpgraphs: + job_id = info2store['solver_details']['job_id'] + solver_name = info2store['creation_options']['solver_name'] + handle = info2store['handle'] + capacity = info2store['capacity'] + dumpname = f'dump_{job_id}_{solver_name}_{handle}_{capacity}' + G_fname = dumpname + '_G.dill' + print(f'Dumping G graph to <{G_fname}>.', file=sys.stderr) + dill.dump(G, open(G_fname, 'wb')) + if partial_solution: + H_fname = dumpname + '_H.dill' + print(f'Dumping H graph to <{H_fname}>.', file=sys.stderr) + dill.dump(pf.H, open(H_fname, 'wb')) + if solver_name == 'gurobi': + solver.close() + elif solver_name == 'cplex': + solver._solver_model.end() + elif solver_name == 'beta': + solver._solver_model.end() + exit(1) + + return H + + +def memory_usage_model_MB(N, solver_name): + mem = 500 * N + 0.8 * N**2 + if solver_name == 'cplex': + return round(mem) + elif solver_name == 'beta': + return round(mem) + else: + return round(mem / 3) + + +def unify_roots(G_base): + ''' + `G_base` is changed in-place. + + Modify the provided nx.Graph `G_base` by reducing its root node to one. + - nonroot nodes and boundary of `G_base` are not changed; + - root nodes of `G_base` are replaced by a single root that is the + centroid of the original ones. + ''' + M = G_base.graph['M'] + if M <= 1: + return + N = G_base.number_of_nodes() - M + VertexC = G_base.graph['VertexC'] + G_base.remove_nodes_from(range(-M, -1)) + G_base.graph['VertexC'] = np.r_[ + VertexC[:N], + VertexC[-M:].mean(axis=0)[np.newaxis, :] + ] + G_base.graph['M'] = M = 1 + G_base.graph['name'] += '.1_OSS' + G_base.graph['handle'] += '_1' + make_graph_metrics(G_base) + return + + +solver_options = {} +# Solver's settings +# Gurobi +solver_options['gurobi'] = dict( + factory=dict( + _name='gurobi', + solver_io='python',), +) +# CPLEX +solver_options['cplex'] = dict( + factory=dict( + _name='cplex', + solver_io='python',), + # threshold for switching node storage strategy + workmem=30000, # in MB + # node storage file switch (activates when workmem is exceeded): + # 0) in-memory + # 1) (the default) fast compression algorithm in-memory + # 2) write to disk + # 3) write to disk and compress + mip_strategy_file=3, + # tree memory limit: + # limit the size of the tree so that it does not exceed available disk + # space, when you choose settings 2 or 3 in the node storage file switch + mip_limits_treememory=50000, # in MB + # directory for working files (if ommited, uses $TMPDIR) + # workdir='/tmp', + workdir=(os.environ.get('TMPDIR') or + os.environ.get('TMP') or + '/tmp'), +) +solver_options['beta'] = solver_options['cplex'] +# Google OR-Tools CP-SAT +solver_options['ortools'] = {} + + +class CondaJob: + ''' + `mem_per_core` and `max_mem` in MB + `time_limit` must be datetime.timedelta + ''' + + def __init__(self, cmdlist, *, conda_env, queue_name, jobname, + mem_per_core, max_mem, cores, time_limit, email=None, + cwd=None, env_variables=None): + self.jobscript = dedent( + f'''\ + #!/usr/bin/env sh + ## queue + #BSUB -q {queue_name} + ## job name + #BSUB -J {jobname} + ## cores + #BSUB -n {cores} + ## cores must be on the same host + #BSUB -R "span[hosts=1]" + ## RAM per core/slot + #BSUB -R "rusage[mem={round(mem_per_core)}MB]" + ## job termination threshold: RAM per core/slot (Resident set size) + #BSUB -M {math.ceil(max_mem)}MB + ## job termination threshold: execution time (hh:mm) + #BSUB -W {':'.join(str(time_limit).split(':')[:2])} + ## stdout + #BSUB -o {jobname}_%J.out + ## stderr + #BSUB -e {jobname}_%J.err + ''') + if cwd is not None: + self.jobscript += dedent( + f'''\ + ## job's current working directory + #BSUB -cwd {cwd} + ''') + if env_variables is not None: + self.jobscript += dedent( + f'''\ + ## environment variables to propagate + #BSUB -env {",".join(env_variables)} + ''') + if email is not None: + self.jobscript += dedent( + f'''\ + ## email + #BSUB -u {email} + ## notify on end + #BSUB -N + ''') + self.jobscript += ' '.join( + [os.environ['CONDA_EXE'], 'run', '--no-capture-output', + '-n', conda_env] + + cmdlist) + self.summary = (f'submitted: {jobname} (# of cores: {cores}, memory: ' + f'{mem_per_core*cores/1000:.1f} GB, time limit: ' + f'{time_limit})') + + def run(self, quiet=False): + subprocess.run(['bsub'], input=self.jobscript.encode()) + if not quiet: + print(self.summary) + + def print(self): + print(self.jobscript) diff --git a/ed_win/plotting_scripts/crossings.py b/ed_win/plotting_scripts/crossings.py new file mode 100644 index 0000000..c0b5af3 --- /dev/null +++ b/ed_win/plotting_scripts/crossings.py @@ -0,0 +1,309 @@ +import operator +import math +import numpy as np +from .geometric import is_same_side, make_graph_metrics +import networkx as nx + + +def get_crossings_list(Edge, VertexC): + ''' + List all crossings between edges in the `Edge` (E×2) numpy array. + Coordinates must be provided in the `VertexC` (V×2) array. + + Used when edges are not limited to the expanded Delaunay set. + ''' + crossings = [] + V = VertexC[Edge[:, 1]] - VertexC[Edge[:, 0]] + for i, ((UVx, UVy), (u, v)) in enumerate(zip(V, Edge[:-1])): + uCx, uCy = VertexC[u] + vCx, vCy = VertexC[v] + for j, ((STx, STy), (s, t)) in enumerate(zip(-V[i + 1:], Edge[i + 1:], + start=i + 1)): + if s == u or t == u or s == v or t == v: + # <edges have a common node> + continue + # bounding box check + sCx, sCy = VertexC[s] + tCx, tCy = VertexC[t] + + # X + lo, hi = (vCx, uCx) if UVx < 0 else (uCx, vCx) + if STx > 0: + if hi < tCx or sCx < lo: + continue + else: + if hi < sCx or tCx < lo: + continue + + # Y + lo, hi = (vCy, uCy) if UVy < 0 else (uCy, vCy) + if STy > 0: + if hi < tCy or sCy < lo: + continue + else: + if hi < sCy or tCy < lo: + continue + + # TODO: save the edges that have interfering bounding boxes + # to be checked in a vectorized implementation of + # the math below + UV = UVx, UVy + ST = STx, STy + + # denominator + f = STx * UVy - STy * UVx + # print('how close: ', f) + # TODO: arbitrary threshold + if math.isclose(f, 0, abs_tol=1e-3): + # segments are parallel + continue + + C = uCx - sCx, uCy - sCy + # alpha and beta numerators + for num in (Px * Qy - Py * Qx for (Px, Py), (Qx, Qy) in ((C, ST), + (UV, C))): + if f > 0: + if less(num, 0) or less(f, num): + continue + else: + if less(0, num) or less(num, f): + continue + + # segments do cross + crossings.append((u, v, s, t)) + return crossings + + +def edgeXing_iter(u, v, G, A): + '''This is broken, do not use! Use `edgeset_edgeXing_iter()` instead.''' + planar = A.graph['planar'] + _, s = A.next_face_half_edge(u, v) + _, t = A.next_face_half_edge(v, u) + if s == t: + # <u, v> and the 3rd vertex are hull + return + if (s, t) in A.edges: + # the diagonal conflicts with the Delaunay edge + yield ((u, v), (s, t)) + conflicting = [(s, t)] + else: + conflicting = [] + # examine the two triangles (u, v) belongs to + for a, b, c in ((u, v, s), + (v, u, t)): + # this is for diagonals crossing diagonals + triangle = tuple(sorted((a, b, c))) + if triangle not in checked: + checked.add(triangle) + _, e = A.next_face_half_edge(c, b) + if (a, e) in A.edges: + conflicting.append((a, e)) + _, d = A.next_face_half_edge(a, c) + if (b, d) in A.edges: + conflicting.append((b, d)) + if len(conflicting) > 1: + yield conflicting + + +def layout_edgeXing_iter(G, A): + '''does this even make sense?''' + for edge in G.edges: + yield from edgeXing_iter(edge, G, A) + + +def edgeset_edgeXing_iter(A): + '''Iterator over all edge crossings in an expanded + Delaunay edge set `A`. Each crossing is a 2 or 3-tuple + of (u, v) edges.''' + P = A.graph['planar'] + diagonals = A.graph['diagonals'] + checked = set() + for (s, t), v in diagonals.items(): + u = P[v][s]['cw'] + triangles = ((u, v, s), (v, u, t)) + u, v = (u, v) if u < v else (v, u) + # crossing with Delaunay edge + yield ((u, v), (s, t)) + # examine the two triangles (u, v) belongs to + for a, b, c in triangles: + triangle = tuple(sorted((a, b, c))) + if triangle in checked: + continue + checked.add(triangle) + # this is for diagonals crossing diagonals + conflicting = [(s, t)] + d = P[c][b]['cw'] + diag_da = (a, d) if a < d else (d, a) + if d == P[b][c]['ccw'] and diag_da in diagonals: + conflicting.append(diag_da) + e = P[a][c]['cw'] + diag_eb = (e, b) if e < b else (b, e) + if e == P[c][a]['ccw'] and diag_eb in diagonals: + conflicting.append(diag_eb) + if len(conflicting) > 1: + yield conflicting + + +def edgeset_edgeXing_iter_deprecated(A, include_roots=False): + '''DEPRECATED! + + Iterator over all edge crossings in an expanded + Delaunay edge set `A`. Each crossing is a 2 or 3-tuple + of (u, v) edges.''' + planar = A.graph['planar'] + checked = set() + # iterate over all Delaunay edges + for u, v in planar.edges: + if u > v or (not include_roots and (u < 0 or v < 0)): + # planar is a DiGraph, so skip one half-edge of the pair + continue + # get diagonal + _, s = planar.next_face_half_edge(u, v) + _, t = planar.next_face_half_edge(v, u) + if s == t or (not include_roots and (s < 0 or t < 0)): + # <u, v> and the 3rd vertex are hull + continue + triangles = [] + if (s, u) in planar.edges: + triangles.append((u, v, s)) + if (t, v) in planar.edges: + triangles.append((v, u, t)) + s, t = (s, t) if s < t else (t, s) + has_diagonal = (s, t) in A.edges + if has_diagonal: + # the diagonal conflicts with the Delaunay edge + yield ((u, v), (s, t)) + # examine the two triangles (u, v) belongs to + for a, b, c in triangles: + # this is for diagonals crossing diagonals + triangle = tuple(sorted((a, b, c))) + if triangle not in checked: + checked.add(triangle) + conflicting = [(s, t)] if has_diagonal else [] + _, e = planar.next_face_half_edge(c, b) + if ((e, c) in planar.edges and + (a, e) in A.edges and + (include_roots or (a >= 0 and e >= 0))): + conflicting.append((a, e) if a < e else (e, a)) + _, d = planar.next_face_half_edge(a, c) + if ((d, a) in planar.edges and + (b, d) in A.edges and + (include_roots or (b >= 0 and d >= 0))): + conflicting.append((b, d) if b < d else (d, b)) + if len(conflicting) > 1: + yield conflicting + + +# adapted edge_crossings() from geometric.py +# delaunay() does not create `triangles` and `triangles_exp` +# anymore, so this is broken +def edgeXing_iter_deprecated(A): + ''' + DEPRECATED! + This is broken, do not use! + + Iterates over all pairs of crossing edges in `A`. This assumes `A` + has only expanded Delaunay edges (with triangles and triangles_exp). + + Used in constraint generation for MILP model. + ''' + triangles = A.graph['triangles'] + # triangles_exp maps expanded Delaunay to Delaunay edges + triangles_exp = A.graph['triangles_exp'] + checked = set() + for uv, (s, t) in triangles_exp.items(): + # <(u, v) is an expanded Delaunay edge> + u, v = uv + checked.add(uv) + if (u, v) not in A.edges: + continue + if (s, t) in A.edges: + yield (((u, v) if u < v else (v, u)), + ((s, t) if s < t else (t, s))) + else: + # this looks wrong... + # the only case where this might happen is + # when a Delaunay edge is removed because of + # the angle > pi/2 blocking of a root node + # but even in this case, we should check for + # crossings with other expanded edges + continue + for a_b in ((u, s), (u, t), (s, v), (t, v)): + if a_b not in triangles: + continue + cd = triangles[frozenset(a_b)] + if cd in checked: + continue + if (cd in triangles_exp and + tuple(cd) in A.edges and + # this last condition is for edges that should have been + # eliminated in delaunay()'s hull_edge_is_overlapping(), + # but weren't + set(triangles_exp[cd]) <= {u, v, s, t}): + c, d = cd + yield (((u, v) if u < v else (v, u)), + ((c, d) if c < d else (d, c))) + + +def gateXing_iter(A, gates=None, touch_is_cross=True): + ''' + Iterate over all crossings between non-gate edges and the edges in `gates`. + If `gates` is not provided, the gates that are not in `A` will be used. + Arguments: + - `gates`: sequence of #root sequences of gate nodes; if None, all nodes + - `touch_is_cross`: if True, count as crossing a gate going over a node + + The order of items in `gates` must correspond to roots in range(-M, 0). + Used in constraint generation for MILP model. + ''' + M = A.graph['M'] + N = A.number_of_nodes() - M + roots = range(-M, 0) + VertexC = A.graph['VertexC'] + anglesRank = A.graph.get('anglesRank', None) + if anglesRank is None: + make_graph_metrics(A) + anglesRank = A.graph['anglesRank'] + anglesXhp = A.graph['anglesXhp'] + anglesYhp = A.graph['anglesYhp'] + # iterable of non-gate edges: + Edge = nx.subgraph_view(A, filter_node=lambda n: n >= 0).edges() + if gates is None: + all_nodes = set(range(N)) + IGate = [] + for r in roots: + nodes = all_nodes.difference(A.neighbors(r)) + IGate.append(np.fromiter(nodes, dtype=int, count=len(nodes))) + else: + IGate = gates + # it is important to consider touch as crossing + # because if a gate goes precisely through a node + # there will be nothing to prevent it from spliting + # that node's subtree + less = operator.le if touch_is_cross else operator.lt + for u, v in Edge: + uC = VertexC[u] + vC = VertexC[v] + for root, iGate in zip(roots, IGate): + rootC = VertexC[root] + uR, vR = anglesRank[u, root], anglesRank[v, root] + highRank, lowRank = (uR, vR) if uR >= vR else (vR, uR) + Xhp = anglesXhp[[u, v], root] + uYhp, vYhp = anglesYhp[[u, v], root] + # get a vector of gate edges' ranks for current root + gaterank = anglesRank[iGate, root] + # check if angle of <u, v> wraps across +-pi + if (not any(Xhp)) and uYhp != vYhp: + # <u, v> wraps across zero + is_rank_within = np.logical_or(less(gaterank, lowRank), + less(highRank, gaterank)) + else: + # <u, v> does not wrap across zero + is_rank_within = np.logical_and(less(lowRank, gaterank), + less(gaterank, highRank)) + for n in iGate[np.flatnonzero(is_rank_within)]: + # this test confirms the crossing because `is_rank_within` + # established that root–n is on a line crossing u–v + if not is_same_side(uC, vC, rootC, VertexC[n]): + u, v = (u, v) if u < v else (v, u) + yield (u, v), (root, n) diff --git a/ed_win/plotting_scripts/dbmodel.py b/ed_win/plotting_scripts/dbmodel.py new file mode 100644 index 0000000..28c5f99 --- /dev/null +++ b/ed_win/plotting_scripts/dbmodel.py @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import datetime +import os + +from pony.orm import Database, IntArray, Optional, PrimaryKey, Required, Set + + +def open_database(filename, create_db=False): + db = Database() + define_entities(db) + db.bind('sqlite', os.path.abspath(os.path.expanduser(filename)), + create_db=create_db) + db.generate_mapping(create_tables=True) + return db + + +def define_entities(db): + ''' + Database model for storage of layouts. + Tables: + - NodeSet: site + - EdgeSet: layout + - Method: info on algorithm & options to produce layouts + - Machine: info on machine that generated a layout + ''' + + class NodeSet(db.Entity): + # hashlib.sha256(VertexC + boundary).digest() + digest = PrimaryKey(bytes) + name = Required(str, unique=True) + N = Required(int) # # of non-root nodes + M = Required(int) # # of root nodes + # vertices (nodes + roots) coordinates (UTM) + # pickle.dumps(np.empty((N + M, 2), dtype=float) + VertexC = Required(bytes) + # region polygon: P vertices (x, y), ordered ccw + # pickle.dumps(np.empty((P, 2), dtype=float) + boundary = Required(bytes) + landscape_angle = Optional(float) + EdgeSets = Set(lambda: EdgeSet) + + class EdgeSet(db.Entity): + nodes = Required(NodeSet) + # edges = pickle.dumps( + # np.array([(u, v) + # for u, v in G.edges], dtype=int)) + edges = Required(bytes) + length = Required(float) + # number of Detour nodes + D = Optional(int, default=0) + clone2prime = Optional(IntArray) + gates = Required(IntArray) + method = Required(lambda: Method) + capacity = Required(int) + # cables = Optional(lambda: CableSet) + runtime = Optional(float) + runtime_unit = Optional(str) + machine = Optional(lambda: Machine) + timestamp = Optional(datetime.datetime, default=datetime.datetime.utcnow) + # DetourC = Optional(bytes) # superceeded by D and clone2prime + # misc is a pickled python dictionary + misc = Optional(bytes) + + class Method(db.Entity): + # hashlib.sha256(funhash + options).digest() + digest = PrimaryKey(bytes) + funname = Required(str) + # hashlib.sha256(esauwilliams.__code__.co_code) + funhash = Required(bytes) + # capacity = Required(int) + # options is a dict of function parameters + options = Required(str) + timestamp = Required(datetime.datetime, default=datetime.datetime.utcnow) + EdgeSets = Set(EdgeSet) + + class Machine(db.Entity): + name = Required(str, unique=True) + EdgeSets = Set(EdgeSet) + + # class CableSet(db.Entity): + # name = Required(str) + # cableset = Required(bytes) + # EdgeSets = Set(EdgeSet) + # max_capacity = Required(int) + # # name = Required(str) + # # types = Required(int) + # # areas = Required(IntArray) # mm² + # # capacities = Required(IntArray) # num of wtg + # # EdgeSets = Set(EdgeSet) + # # max_capacity = Required(int) diff --git a/ed_win/plotting_scripts/farmrepo.py b/ed_win/plotting_scripts/farmrepo.py new file mode 100644 index 0000000..3a30280 --- /dev/null +++ b/ed_win/plotting_scripts/farmrepo.py @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +from importlib.resources import files + +import numpy as np + +from .fileio import file2graph +from .utils import namedtuplify +from .synthetic import equidistant, synthfarm2graph + + +def tess(radius, spacing=1000): + NodeC = equidistant(radius, center='centroid', spacing=spacing) + RootC = np.zeros((1, 2), dtype=float) + return synthfarm2graph(RootC, NodeC, name='SynthTess', handle='tess') + + +def tess3(radius, spacing=1000): + h = np.sqrt(3) / 2 + NodeC = equidistant(radius, center='vertex', spacing=spacing) + RootC = 5 * spacing * np.array(((3 / 4, -h / 2), (-3 / 4, -h / 2), (0., h))) + return synthfarm2graph(RootC, NodeC, name='SynthTess (3 OSS)', + handle='tess3') + + +def tess3sm(radius, spacing=1000): + h = np.sqrt(3) / 2 + NodeC = equidistant(radius, center='vertex', spacing=spacing) + RootC = 2.5 * spacing * np.array(((-0.5, -h), (-0.5, h), (1., 0.))) + return synthfarm2graph(RootC, NodeC, name='SynthTess small (3 OSS)', + handle='tess3m') + + +datapath = files('interarray.data') + +g = namedtuplify( + namedtuple_typename='FarmGraphs', + # .xlsx files + # 100 WTG + thanet=file2graph(datapath / 'Thanet.xlsx', handle='thanet'), + # 80 WTG + dantysk=file2graph(datapath / 'DanTysk.xlsx', handle='dantysk'), + # 80 WTG + horns=file2graph(datapath / 'Horns Rev 1.xlsx', handle='horns'), + # 111 WTG + anholt=file2graph(datapath / 'Anholt.xlsx', handle='anholt'), + # 108 WTG + sands=file2graph(datapath / 'West of Duddon Sands.xlsx', handle='sands'), + # 30 WTG + ormonde=file2graph(datapath / 'Ormonde.xlsx', handle='ormonde'), + # 175 WTG, 2 OSS + london=file2graph(datapath / 'London Array.xlsx', handle='london'), + # 27 WTG + rbn=file2graph(datapath / 'BIG Ronne Bank North.xlsx', handle='rbn'), + # 53 WTG + rbs=file2graph(datapath / 'BIG Ronne Bank South.xlsx', handle='rbs'), + + # synthetic farms + # 114 WTG + tess=tess(radius=5600), + # 241 WTG, 3 OSS + tess3=tess3(radius=8000), + # ? WTG, 3 OSS + # tess3sm=tess3sm(radius=5300), +) diff --git a/ed_win/plotting_scripts/farmrepo_landscape.py b/ed_win/plotting_scripts/farmrepo_landscape.py new file mode 100644 index 0000000..10c901f --- /dev/null +++ b/ed_win/plotting_scripts/farmrepo_landscape.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +from importlib.resources import files + +import numpy as np + +from .fileio import file2graph +from .utils import namedtuplify +from .synthetic import equidistant, synthfarm2graph + + +def tess(radius, spacing=1000): + NodeC = equidistant(radius, center='centroid', spacing=spacing) + RootC = np.zeros((1, 2), dtype=float) + return synthfarm2graph(RootC, NodeC, name='SynthTess', handle='tess') + + +def tess3(radius, spacing=1000): + h = np.sqrt(3) / 2 + NodeC = equidistant(radius, center='vertex', spacing=spacing) + RootC = 5 * spacing * np.array(((3 / 4, -h / 2), (-3 / 4, -h / 2), (0., h))) + return synthfarm2graph(RootC, NodeC, name='SynthTess (3 OSS)', + handle='tess3') + + +def tess3sm(radius, spacing=1000): + h = np.sqrt(3) / 2 + NodeC = equidistant(radius, center='vertex', spacing=spacing) + RootC = 2.5 * spacing * np.array(((-0.5, -h), (-0.5, h), (1., 0.))) + return synthfarm2graph(RootC, NodeC, name='SynthTess small (3 OSS)', + handle='tess3sm') + + +datapath = files('interarray.data') + +g = namedtuplify( + namedtuple_typename='FarmGraphs', + # .xlsx files + # 100 WTG + thanet=file2graph(datapath / 'Thanet.xlsx', rotation=49, handle='thanet'), + # 80 WTG + dantysk=file2graph(datapath / 'DanTysk.xlsx', rotation=90, + handle='dantysk'), + # 80 WTG + horns=file2graph(datapath / 'Horns Rev 1.xlsx', handle='horns'), + # 111 WTG + anholt=file2graph(datapath / 'Anholt.xlsx', rotation=84, handle='anholt'), + # 108 WTG + sands=file2graph(datapath / 'West of Duddon Sands.xlsx', rotation=55, + handle='sands'), + # 30 WTG + ormonde=file2graph(datapath / 'Ormonde.xlsx', rotation=45, + handle='ormonde'), + # 175 WTG, 2 OSS + london=file2graph(datapath / 'London Array.xlsx', rotation=-95, + handle='london'), + # 27 WTG + rbn=file2graph(datapath / 'BIG Ronne Bank North.xlsx', rotation=-4, + handle='rbn'), + # 53 WTG + rbs=file2graph(datapath / 'BIG Ronne Bank South.xlsx', rotation=-2, + handle='rbs'), + + # synthetic farms + # 114 WTG + tess=tess(radius=5600), + # 241 WTG, 3 OSS + tess3=tess3(radius=8000), + # ? WTG, 3 OSS + # tess3sm=tess3sm(radius=5300), +) diff --git a/ed_win/plotting_scripts/fileio.py b/ed_win/plotting_scripts/fileio.py new file mode 100644 index 0000000..a5603e5 --- /dev/null +++ b/ed_win/plotting_scripts/fileio.py @@ -0,0 +1,123 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +from pathlib import Path + +import networkx as nx +import numpy as np +import scipy.io +import utm +import yaml +from openpyxl import load_workbook + +from .geometric import make_graph_metrics, rotate +from .utils import NodeTagger + + +F = NodeTagger() + + +def utm_from_latlonstr(entries): + utm_out = [] + for entry in entries.splitlines(): + label, lat, lon = entry.split(' ') + coords = [] + for ll in (lat, lon): + val, hemisphere = ll.split("'") + deg, sec = val.split('°') + coords.append((float(deg) + float(sec) / 60) * (1 if hemisphere in ('N', 'E') else -1)) + utm_out.append((label, *utm.from_latlon(*coords))) + return utm_out + + +def file2graph(filename, rotation=None, handle='file'): + '''filename is a Matlab .mat file or an Excel + spreadsheet in the proper format''' + fpath = Path(filename) + data = {} + # read Excel xls file + if fpath.suffix == '.xls': + print('xls reading not implemented, save as .xlsx instead') + return None + # read wind power plant YAML file + elif fpath.suffix == '.yaml': + scrapped = yaml.safe_load(open(fpath, 'r', encoding='utf8')) + for key, scrapped_key in (('WT coordinates', 'TURBINES'), + ('OSS coordinates', 'SUBSTATIONS'), + ('WF area limits', 'EXTENTS')): + source = utm_from_latlonstr(scrapped[scrapped_key]) + labels, easting, northing, zone_number, zone_letter = zip(*source) + xy = np.array((easting, northing)) + if rotation is not None: + coords = rotate(xy.T, rotation).T + else: + coords = xy + data[key] = coords, labels + # read Excel xlsx file + elif fpath.suffix == '.xlsx': + wb = load_workbook(filename=fpath, read_only=True, data_only=True) + for ws in wb.worksheets[:4]: + key = ws['A1'].value + if key not in ['WF area limits', 'OSS coordinates', + 'WT coordinates', 'Forbidden Zones']: + continue + if key == 'Forbidden Zones': + if ws['A3'].value is not None: + print('Forbidden Zones not yet implemented.') + continue + for cell, header in (('A2', 'x (m)'), + ('B2', 'y (m)')): + assert ws[cell].value == header + xy = [(float(x.value), float(y.value)) for x, y in + ws.iter_rows(min_row=3, min_col=1, max_col=2) if + ((x.data_type == 'n') and + (y.data_type == 'n') and + (x.value is not None) and + (y.value is not None))] + labels = [lab.value for lab, in + ws.iter_rows(min_row=3, min_col=3, max_col=3) + if lab.value is not None] + if len(xy) != len(labels): + labels = None + if xy: + if rotation is not None: + coords = rotate(np.array(xy), rotation).T + else: + coords = np.array(tuple(zip(*xy)), dtype=float) + data[key] = coords, labels + # read Matlab mat file + elif fpath.suffix == '.mat': + windfarm = scipy.io.loadmat(fpath, + struct_as_record=False, + squeeze_me=True)['WindFarm'] + data['WT coordinates'] = (np.r_[[windfarm.Coord.x[1:]], + [windfarm.Coord.y[1:]]], + None) + data['WF area limits'] = (np.r_[[windfarm.Area.xv], + [windfarm.Area.yv]], + None) + # TODO: is the matlab data structure compatible with multiple OSS? + data['OSS coordinates'] = (np.r_[[windfarm.Coord.x[0]], + [windfarm.Coord.y[0]]], + None) + + # TODO: add node labels/ids to graph properties, if given + + # build data structures + WTcoords, WTlabels = data['WT coordinates'] + OSScoords, OSSlabels = data['OSS coordinates'] + boundary = data['WF area limits'][0].T + N = WTcoords.shape[1] + M = OSScoords.shape[1] + # create networkx graph + G = nx.Graph(M=M, + VertexC=np.vstack((WTcoords.T, OSScoords.T[::-1])), + boundary=boundary, + name=fpath.stem, + handle=handle) + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + make_graph_metrics(G) + return G diff --git a/ed_win/plotting_scripts/geometric.py b/ed_win/plotting_scripts/geometric.py new file mode 100644 index 0000000..25c095e --- /dev/null +++ b/ed_win/plotting_scripts/geometric.py @@ -0,0 +1,1152 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import functools +import math +import operator +from collections import defaultdict +from itertools import chain, product +from math import isclose +from typing import Tuple + +import shapely as shp +import networkx as nx +import numpy as np +from scipy.sparse import coo_array +from scipy.sparse.csgraph import minimum_spanning_tree as scipy_mst +from scipy.spatial import Delaunay +from scipy.spatial.distance import cdist + +from . import MAX_TRIANGLE_ASPECT_RATIO +from .utils import NodeStr, NodeTagger + +F = NodeTagger() + + +def triangle_AR(uC, vC, tC): + '''returns the aspect ratio of the triangle defined by the three 2D points + `uC`, `vC` and `tC`, which must be numpy arrays''' + lengths = np.hypot(*np.column_stack((vC - tC, tC - uC, uC - vC))) + den = (lengths.sum() / 2 - lengths).prod() + if den == 0.: + return float('inf') + else: + return lengths.prod() / 8 / den + + +def any_pairs_opposite_edge(NodesC, uC, vC, margin=0): + '''Returns True if any two of `NodesC` are on opposite + sides of the edge (`uC`, `vC`). + ''' + maxidx = len(NodesC) - 1 + if maxidx <= 0: + return False + refC = NodesC[0] + i = 1 + while point_d2line(refC, uC, vC) <= margin: + # ref node is approx. overlapping the edge: get the next one + refC = NodesC[i] + i += 1 + if i > maxidx: + return False + + for cmpC in NodesC[i:]: + if point_d2line(cmpC, uC, vC) <= margin: + # cmp node is approx. overlapping the edge: skip + continue + if not is_same_side(uC, vC, refC, cmpC, + touch_is_cross=False): + return True + return False + + +def rotate(coords, angle): + '''rotates `coords` (numpy array N×2) by `angle` (degrees)''' + rotation = np.deg2rad(angle) + c, s = np.cos(rotation), np.sin(rotation) + return np.dot(coords, np.array([[c, s], [-s, c]])) + + +def point_d2line(p, u, v): + ''' + Calculate the distance from point `p` to the line defined by points `u` + and `v`. + ''' + x0, y0 = p + x1, y1 = u + x2, y2 = v + return (abs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)) / + math.sqrt((x2 - x1)**2 + (y2 - y1)**2)) + + +def angle_numpy(a, pivot, b): + '''a, pivot, b are coordinate pairs + returns angle a-root-b (radians) + - angle is within ±π (shortest arc from a to b around pivot) + - positive direction is counter-clockwise''' + A = a - pivot + B = b - pivot + # dot_prod = np.dot(A, B) if len(A) >= len(B) else np.dot(B, A) + dot_prod = A @ B.T # if len(A) >= len(B) else np.dot(B, A) + # return np.arctan2(np.cross(A, B), np.dot(A, B)) + return np.arctan2(np.cross(A, B), dot_prod) + + +def angle(a, pivot, b): + '''`a`, `pivot`, `b` are coordinate pairs + returns angle a-root-b (radians) + - angle is within ±π (shortest arc from a to b around pivot) + - positive direction is counter-clockwise''' + Ax, Ay = a - pivot + Bx, By = b - pivot + # debug and print(VertexC[a], VertexC[b]) + ang = np.arctan2(Ax * By - Ay * Bx, Ax * Bx + Ay * By) + # debug and print(f'{ang*180/np.pi:.1f}') + return ang + + +def is_bb_overlapping(uv, wy): + ''' checks if there is an overlap in the bounding boxes of `uv` and `wy` + (per row) + `uv` and `wy` have shape N×2, ''' + pass + + +def is_crossing_numpy(u, v, w, y): + '''checks if (u, v) crosses (w, y); + returns ¿? in case of superposition''' + + # adapted from Franklin Antonio's insectc.c lines_intersect() + # Faster Line Segment Intersection + # Graphics Gems III (http://www.graphicsgems.org/) + # license: https://github.com/erich666/GraphicsGems/blob/master/LICENSE.md + + A = v - u + B = w - y + + # bounding box check + for i in (0, 1): # X and Y + lo, hi = (v[i], u[i]) if A[i] < 0 else (u[i], v[i]) + if B[i] > 0: + if hi < y[i] or w[i] < lo: + return False + else: + if hi < w[i] or y[i] < lo: + return False + + C = u - w + + # denominator + f = np.cross(B, A) + if f == 0: + # segments are parallel + return False + + # alpha and beta numerators + for num in (np.cross(P, Q) for P, Q in ((C, B), (A, C))): + if f > 0: + if num < 0 or num > f: + return False + else: + if num > 0 or num < f: + return False + + # code to calculate intersection coordinates omitted + # segments do cross + return True + + +def is_crossing(u, v, w, y, touch_is_cross=True): + '''checks if (u, v) crosses (w, y); + returns ¿? in case of superposition + choices for `less`: + -> operator.lt counts touching as crossing + -> operator.le does not count touching as crossing + ''' + less = operator.lt if touch_is_cross else operator.le + + # adapted from Franklin Antonio's insectc.c lines_intersect() + # Faster Line Segment Intersection + # Graphic Gems III + + A = v - u + B = w - y + + # bounding box check + for i in (0, 1): # X and Y + lo, hi = (v[i], u[i]) if A[i] < 0 else (u[i], v[i]) + if B[i] > 0: + if hi < y[i] or w[i] < lo: + return False + else: + if hi < w[i] or y[i] < lo: + return False + + Ax, Ay = A + Bx, By = B + Cx, Cy = C = u - w + + # denominator + # print(Ax, Ay, Bx, By) + f = Bx * Ay - By * Ax + # print('how close: ', f) + # TODO: arbitrary threshold + if isclose(f, 0, abs_tol=1e-3): + # segments are parallel + return False + + # alpha and beta numerators + for num in (Px * Qy - Py * Qx for (Px, Py), (Qx, Qy) in ((C, B), (A, C))): + if f > 0: + if less(num, 0) or less(f, num): + return False + else: + if less(0, num) or less(num, f): + return False + + # code to calculate intersection coordinates omitted + # segments do cross + return True + + +def is_bunch_split_by_corner(bunch, a, o, b, margin=1e-3): + '''`bunch` is a numpy array of points (N×2) + the points `a`-`o`-`b` define a corner''' + AngleA = angle_numpy(a, o, bunch) + AngleB = angle_numpy(b, o, bunch) + # print('AngleA', AngleA, 'AngleB', AngleB) + # keep only those that don't fall over the angle-defining lines + keep = ~np.logical_or(np.isclose(AngleA, 0, atol=margin), + np.isclose(AngleB, 0, atol=margin)) + angleAB = angle(a, o, b) + angAB = angleAB > 0 + inA = AngleA > 0 if angAB else AngleA < 0 + inB = AngleB > 0 if ~angAB else AngleB < 0 + # print(angleAB, keep, inA, inB) + inside = np.logical_and(keep, np.logical_and(inA, inB)) + outside = np.logical_and(keep, np.logical_or(~inA, ~inB)) + split = any(inside) and any(outside) + return split, np.flatnonzero(inside), np.flatnonzero(outside) + + +def is_triangle_pair_a_convex_quadrilateral(u, v, s, t): + '''⟨u, v⟩ is the common side; + ⟨s, t⟩ are the opposing vertices; + returns False also if it is a triangle + only works if ⟨s, t⟩ crosses the line defined by ⟨u, v⟩''' + # this used to be called `is_quadrilateral_convex()` + # us × ut + usut = np.cross(s - u, t - u) + # vt × vs + vtvs = np.cross(t - v, s - v) + if usut == 0 or vtvs == 0: + # the four vertices form a triangle + return False + return (usut > 0) == (vtvs > 0) + + +def is_same_side(L1, L2, A, B, touch_is_cross=True): + '''Check if points A an B are on the same side + of the line defined by points L1 and L2. + + Note: often used to check crossings with gate edges, + where the gate edge A-B is already known to be on a line + that crosses the edge L1–L2 (using the angle rank).''' + + # greater = operator.gt if touch_is_cross else operator.ge + greater = operator.ge if touch_is_cross else operator.gt + # print(L1, L2, A, B) + (Ax, Ay), (Bx, By), (L1x, L1y), (L2x, L2y) = (A, B, L1, L2) + denom = (L1x - L2x) + # test to avoid division by zero + if denom: + a = -(L1y - L2y) / denom + c = -a * L1x - L1y + num = a * Ax + Ay + c + den = a * Bx + By + c + discriminator = num * den + else: + # this means the line is vertical (L1x = L2x) + # which makes the test simpler + discriminator = (Ax - L1x) * (Bx - L1x) + return greater(discriminator, 0) + + +def is_blocking(root, u, v, w, y): + # w and y are necessarily on opposite sides of uv + # (because of Delaunay – see the triangles construction) + # hence, if (root, y) are on the same side, (w, root) are not + return (is_triangle_pair_a_convex_quadrilateral(u, v, w, root) + if is_same_side(u, v, root, y) + else is_triangle_pair_a_convex_quadrilateral(u, v, root, y)) + + +def apply_edge_exemptions(G, allow_edge_deletion=True): + ''' + should be DEPRECATED (depends on `delaunay_deprecated()`'s triangles) + + exemption is used by weighting functions that take + into account the angular sector blocked by each edge w.r.t. + the closest root node + ''' + E_hull = G.graph['E_hull'] + N_hull = G.graph['N_hull'] + N_inner = set(G.nodes) - N_hull + M = G.graph['M'] + # N = G.number_of_nodes() - M + VertexC = G.graph['VertexC'] + # roots = range(N, N + M) + roots = range(-M, 0) + triangles = G.graph['triangles'] + angles = G.graph['angles'] + + # set hull edges as exempted + for edge in E_hull: + G.edges[edge]['exempted'] = True + + # expanded E_hull to contain edges exempted from blockage penalty + # (edges that do not block line from nodes to root) + E_hull_exp = E_hull.copy() + + # check if edges touching the hull should be exempted from blockage penalty + for n_hull in N_hull: + for n_inner in (N_inner & set([v for u, v in G.edges(n_hull)])): + uv = frozenset((n_hull, n_inner)) + u, v = uv + opposites = triangles[uv] + if len(opposites) == 2: + w, y = triangles[uv] + rootC = VertexC[G.edges[u, v]['root']] + uvwyC = tuple((VertexC[n] for n in (*uv, w, y))) + if not is_blocking(rootC, *uvwyC): + E_hull_exp.add(uv) + G.edges[uv]['exempted'] = True + + # calculate blockage arc for each edge + zeros = np.full((M,), 0.) + for u, v, d in list(G.edges(data=True)): + if (frozenset((u, v)) in E_hull_exp) or (u in roots) or (v in roots): + angdiff = zeros + else: + # angdiff = (angles[:, u] - angles[:, v]) % (2*np.pi) + # angdiff = abs(angles[:, u] - angles[:, v]) + angdiff = abs(angles[u] - angles[v]) + arc = np.empty((M,), dtype=float) + for i in range(M): # TODO: vectorize this loop + arc[i] = angdiff[i] if angdiff[i] < np.pi else 2 * np.pi - angdiff[i] + d['arc'] = arc + # if arc is π/2 or more, remove the edge (it's shorter to go to root) + if allow_edge_deletion and any(arc >= np.pi / 2): + G.remove_edge(u, v) + print('angles', arc, 'removing «', + '–'.join([F[n] for n in (u, v)]), '»') + + +def edge_crossings(s, t, G, diagonals, P): + s, t = (s, t) if s < t else (t, s) + v = diagonals.get((s, t)) + crossings = [] + if v is None: + # ⟨s, t⟩ is a Delaunay edge + Pst = P[s][t] + Pts = P[t][s] + u = Pst['cw'] + v = Pts['cw'] + if u == Pts['ccw'] and v == Pst['ccw']: + diag = (u, v) if u < v else (v, u) + if diag in diagonals and diag in G.edges: + crossings.append(diag) + else: + # ⟨s, t⟩ is a diagonal + u = P[v][s]['cw'] + triangles = ((u, v, s), (v, u, t)) + u, v = (u, v) if u < v else (v, u) + # crossing with Delaunay edge + crossings.append((u, v)) + # examine the two triangles (u, v) belongs to + for a, b, c in triangles: + # this is for diagonals crossing diagonals + d = P[c][b]['cw'] + diag_da = (a, d) if a < d else (d, a) + if d == P[b][c]['ccw'] and diag_da in diagonals: + crossings.append(diag_da) + e = P[a][c]['cw'] + diag_eb = (e, b) if e < b else (b, e) + if e == P[c][a]['ccw'] and diag_eb in diagonals: + crossings.append(diag_eb) + return [edge for edge in crossings if edge in G.edges] + + +def make_planar_embedding(M: int, VertexC: np.ndarray, BoundaryC=None, + max_tri_AR=MAX_TRIANGLE_ASPECT_RATIO): + V = VertexC.shape[0] + N = V - M + SwappedVertexC = np.vstack((VertexC[-M:], VertexC[:N])) + tri = Delaunay(SwappedVertexC) + + NULL = np.iinfo(tri.simplices.dtype).min + mat = np.full((V, V), NULL, dtype=tri.simplices.dtype) + + S = tri.simplices - M + # simplices (i.e. triangles) are oriented ccw + # mat[u, v] returns the next ccw vertice following v + mat[S[:, 0], S[:, 1]] = S[:, 2] + mat[S[:, 1], S[:, 2]] = S[:, 0] + mat[S[:, 2], S[:, 0]] = S[:, 1] + del S + + # Delaunay() produces a convex hull, but it is edge-based and unordered. + # Use that to make an array of nodes defining the convex hull in ccw order. + # this will make all hull_edges point ccw + hull_edges = np.array( + [(u, v) if mat[v, u] == NULL else (v, u) + for u, v in (tri.convex_hull - M)], + dtype=[('src', int), ('dst', int)]) + hull_edges.sort(order='src') + cur = start = hull_edges['src'][0] + next = hull_edges['dst'][0] + hull_vertices = [cur] + while next != start: + cur = next + next = hull_edges['dst'][hull_edges['src'].searchsorted(cur)] + hull_vertices.append(cur) + + # getting rid of nearly flat Delaunay triangles + # qhull (used by scipy) seems not able to do it + # reference: http://www.qhull.org/html/qh-faq.htm#flat + hull_stack = hull_vertices[0:1] + hull_vertices[::-1] + u, v = hull_vertices[-1], hull_stack.pop() + hull_prunned = [] + while hull_stack: + t = mat[u, v] + AR = triangle_AR(*VertexC[(u, v, t),]) + # TODO: document this relaxation of max_tri_AR for root nodes + # (i.e. when considering root nodes, be less strict with AR) + if AR <= max_tri_AR or (min(u, v, t) < 0 and AR < 50 * max_tri_AR): + hull_prunned.append(v) + u = v + v = hull_stack.pop() + else: + mat[u, v] = mat[v, t] = mat[t, u] = NULL + hull_stack.append(v) + v = t + + # prevent edges that cross the boudaries from going into PlanarEmbedding + # an exception is made for edges that include a root node + hull_concave = [] + if BoundaryC is not None: + singled_nodes = {} + hull_prunned_poly = shp.Polygon(VertexC[hull_prunned]) + shp.prepare(hull_prunned_poly) + bound_poly = shp.Polygon(BoundaryC) + shp.prepare(bound_poly) + if not bound_poly.covers(hull_prunned_poly): + hull_stack = hull_prunned[0:1] + hull_prunned[::-1] + u, v = hull_prunned[-1], hull_stack.pop() + while hull_stack: + edge_line = shp.LineString(VertexC[[u, v]]) + if (u >= 0 and v >= 0 and + not bound_poly.covers(edge_line)): + t = mat[u, v] + if t == NULL: + # degenerate case 1 + singled_nodes[v] = u + hull_concave.append(v) + t = v + v = u + u = t + continue + mat[u, v] = mat[v, t] = mat[t, u] = NULL + if t in hull_prunned: + # degenerate case 2 + singled_nodes[u] = t + hull_concave.append(t) + u = t + continue + hull_stack.append(v) + v = t + else: + hull_concave.append(v) + u = v + v = hull_stack.pop() + if not hull_concave: + hull_concave = hull_prunned + + # find the hull for non-root nodes only + hull_stack = hull_concave[2:0:-1] + hull_concave[::-1] + u, v = hull_concave[-1], hull_stack.pop() + hull_nonroot = [] + while len(hull_stack) > 1: + if v < 0: + # v is a root + s = hull_stack[-1] + if ((np.cross(VertexC[s] - VertexC[v], + VertexC[v] - VertexC[u]) < 0) or + (BoundaryC is not None and + not bound_poly.covers(shp.Point(VertexC[v])))): + # This root should not be inside hull_nonroot. Either the + # angle at v is > π or the root is outside the boundary. + # This segment of hull_nonroot follows v's neighbors. + while True: + u = mat[u, v] + if u < 0: + # TODO: handle this case + raise NotImplementedError( + "2 roots are Delaunay neighbors and hull+nonhull." + ) + if u == s: + u = hull_nonroot[-1] + break + hull_nonroot.append(u) + else: + # 〈u, v, s〉 is not convex + u = v + hull_nonroot.append(v) + else: + # v is not a root + u = v + hull_nonroot.append(v) + v = hull_stack.pop() + + planar = nx.PlanarEmbedding(hull=hull_vertices, + hull_prunned=hull_prunned, + hull_concave=hull_concave, + hull_nonroot=hull_nonroot) + planar.add_nodes_from(range(-M, N)) + # add planar embedding half-edges, using + # Delaunay triangles (vertices in ccw order) + # triangles are stored in `mat` + # i.e. mat[u, v] == t if u, v, t are vertices + # of a triangle in ccw order + # for u, next_ in enumerate(mat, start=-M): + + # diagonals store an edge ⟨s, t⟩ as key (s < t) + # and a reference node `v` as value; to add a + # diagonal to a PlanarEmbedding use these two lines: + # PlanarEmbedding.add_half_edge_ccw(s, t, v) + # PlanarEmbedding.add_half_edge_cw(t, s, v) + # to find u, one can use: + # _, u = PlanarEmbedding.next_face_half_edge(v, s) + # or: + # u = PlanarEmbedding[v][s]['cw'] + diagonals = {} + for u, next_ in zip(chain(range(N), range(-M, 0)), mat): + # first rotate ccw, so next_ is a row + # get any of node's edge + # argmax() of boolean may use shortcircuiting logic + # which means it would stop searching on the first True + first = (next_ >= -M).argmax() + if first == 0 and next_[0] == NULL: + # degenerate case + v = singled_nodes[u] + print('degenerate:', F[u], F[v]) + planar.add_half_edge_first(u, v) + continue + first = first % N - M * (first // N) + v = first + back = mat[v, u] + fwd = next_[v] + planar.add_half_edge_first(u, v) + if back != NULL and fwd != NULL: + uC, vC, fwdC, backC = VertexC[(u, v, fwd, back),] + s, t = (back, fwd) if back < fwd else (fwd, back) + if ((s, t) not in diagonals and + triangle_AR(fwdC, uC, backC) < max_tri_AR and + triangle_AR(fwdC, vC, backC) < max_tri_AR and + is_triangle_pair_a_convex_quadrilateral(uC, vC, backC, + fwdC)): + diagonals[(s, t)] = v if s == back else u + # start by circling vertex u in ccw direction + add = planar.add_half_edge_ccw + ccw = True + # when fwd == first, all triangles around vertex u have been visited + while fwd != first: + if fwd != NULL: + back = v + v = fwd + fwd = next_[v] + add(u, v, back) + if fwd != NULL: + uC, vC, fwdC, backC = VertexC[(u, v, fwd, back),] + s, t = (back, fwd) if back < fwd else (fwd, back) + if ((s, t) not in diagonals and + triangle_AR(fwdC, uC, backC) < max_tri_AR and + triangle_AR(fwdC, vC, backC) < max_tri_AR and + is_triangle_pair_a_convex_quadrilateral( + uC, vC, backC, fwdC)): + if ccw: + diagonals[(s, t)] = v if s == back else u + else: + diagonals[(s, t)] = u if s == back else v + elif ccw: + # ccw direction reached the convex hull + # start from first again in cw direction + add = planar.add_half_edge_cw + ccw = False + # when going cw, next_ is a column + next_ = mat[:, u] + back = mat[u, first] + v = first + fwd = next_[v] + else: + # cw direction ended at the convex hull + break + if BoundaryC is not None: + # add the other half-edge for degenerate cases + for u, v in singled_nodes.items(): + i = hull_concave.index(v) + ref = hull_concave[i - 1] + planar.add_half_edge_ccw(v, u, ref) + del mat + # raise an exception if `planar` is not proper: + planar.check_structure() + return planar, diagonals + + +def perimeter(VertexC, vertices_ordered): + ''' + `vertices_ordered` represent indices of `VertexC` in clockwise or counter- + clockwise order. + ''' + vec = VertexC[vertices_ordered[:-1]] - VertexC[vertices_ordered[1:]] + return (np.hypot(*vec.T).sum() + + np.hypot(*(VertexC[vertices_ordered[-1]] - + VertexC[vertices_ordered[0]]))) + + +def delaunay(G_base, add_diagonals=True, debug=False, bind2root=False, + max_tri_AR=MAX_TRIANGLE_ASPECT_RATIO, **qhull_options): + '''Create a new networkx.Graph from the Delaunay triangulation of the + coordinate positions of the vertices in `G_base`. Each edge gets an + attribute `length` that is the euclidean distance between its vertices.''' + M = G_base.graph['M'] + VertexC = G_base.graph['VertexC'] + N = VertexC.shape[0] - M + relax_boundary = G_base.graph.get('relax_boundary', False) + BoundaryC = None if relax_boundary else G_base.graph.get('boundary', None) + + planar, diagonals = make_planar_embedding( + M, VertexC, BoundaryC=BoundaryC, max_tri_AR=max_tri_AR) + + # undirected Delaunay edge view + undirected = planar.to_undirected(as_view=True) + + # build the undirected graph + A = nx.Graph() + A.add_nodes_from(((n, {'label': label}) + for n, label in G_base.nodes(data='label') + if 0 <= n < N), type='wtg') + for r in range(-M, 0): + A.add_node(r, label=G_base.nodes[r]['label'], type='oss') + A.add_edges_from(undirected.edges, type='delaunay') + E_planar = np.array(undirected.edges, dtype=int) + Length = np.hypot(*(VertexC[E_planar[:, 0]] - VertexC[E_planar[:, 1]]).T) + for (u, v), length in zip(E_planar, Length): + A[u][v]['length'] = length + if add_diagonals: + diagnodes = np.empty((len(diagonals), 2), dtype=int) + for row, uv in zip(diagnodes, diagonals): + row[:] = uv + A.add_edges_from(diagonals, type='extended') + # the reference vertex `v` that `diagonals` carries + # could be stored as edge ⟨s, t⟩'s property (that + # property would also mark the edge as a diagonal) + Length = np.hypot(*(VertexC[diagnodes[:, 0]] - + VertexC[diagnodes[:, 1]]).T) + for (u, v), length in zip(diagnodes, Length): + A[u][v]['length'] = length + + d2roots = G_base.graph.get('d2roots') + if d2roots is None: + d2roots = cdist(VertexC[:-M], VertexC[-M:]) + if bind2root: + for n, n_root in G_base.nodes(data='root'): + A.nodes[n]['root'] = n_root + # alternatively, if G_base nodes do not have 'root' attr: + # for n, nodeD in A.nodes(data=True): + # nodeD['root'] = -M + np.argmin(d2roots[n]) + # assign each edge to the root closest to the edge's middle point + for u, v, edgeD in A.edges(data=True): + edgeD['root'] = -M + np.argmin( + cdist(((VertexC[u] + VertexC[v]) / 2)[np.newaxis, :], + VertexC[-M:])) + A.graph.update(M=M, + VertexC=VertexC, + planar=planar, + d2roots=d2roots, + diagonals=diagonals, + hull=planar.graph['hull'], + landscape_angle=G_base.graph.get('landscape_angle', 0), + boundary=G_base.graph['boundary'], + name=G_base.graph['name'], + handle=G_base.graph['handle']) + + # TODO: update other code that uses the data below + # old version of delaunay() also stored these: + # save the convex hull node set + # G.graph['N_hull'] = N_hull # only in geometric.py + # save the convex hull edge set + # G.graph['E_hull'] = E_hull # only in geometric.py + + # these two are more important, as they are used in EW + # variants that do crossing checks (calls to `edge_crossings()`) + # G.graph['triangles'] = triangles + # G.graph['triangles_exp'] = triangles_exp + return A + + +def make_graph_metrics(G): + ''' + This function changes G in place! + Calculates for all nodes, for each root node: + - distance to root nodes + - angle wrt root node + + Any detour nodes in G are ignored. + ''' + VertexC = G.graph['VertexC'] + M = G.graph['M'] + # N = G.number_of_nodes() - M + roots = range(-M, 0) + NodeC = VertexC[:-M] + RootC = VertexC[-M:] + + # calculate distance from all nodes to each of the roots + d2roots = cdist(VertexC[:-M], VertexC[-M:]) + + angles = np.empty_like(d2roots) + for n, nodeC in enumerate(NodeC): + nodeD = G.nodes[n] + # assign the node to the closest root + nodeD['root'] = -M + np.argmin(d2roots[n]) + x, y = (nodeC - RootC).T + angles[n] = np.arctan2(y, x) + # TODO: ¿is this below actually used anywhere? + # assign root nodes to themselves (for completeness?) + for root in roots: + G.nodes[root]['root'] = root + + G.graph['d2roots'] = d2roots + G.graph['d2rootsRank'] = np.argsort(np.argsort(d2roots, axis=0), axis=0) + G.graph['angles'] = angles + G.graph['anglesRank'] = np.argsort(np.argsort(angles, axis=0), axis=0) + G.graph['anglesYhp'] = angles >= 0. + G.graph['anglesXhp'] = abs(angles) < np.pi / 2 + + +# TODO: get new implementation from Xings.ipynb +# xingsmat, edge_from_Eidx, Eidx__ +def get_crossings_map(Edge, VertexC, prune=True): + crossings = defaultdict(list) + for i, A in enumerate(Edge[:-1]): + u, v = A + uC, vC = VertexC[A] + for B in Edge[i + 1:]: + s, t = B + if s == u or t == u or s == v or t == v: + # <edges have a common node> + continue + sC, tC = VertexC[B] + if is_crossing(uC, vC, sC, tC): + crossings[frozenset((*A,))].append((*B,)) + crossings[frozenset((*B,))].append((*A,)) + return crossings + + +# TODO: test this implementation +def complete_graph(G_base, include_roots=False, prune=True, crossings=False): + '''Creates a networkx graph connecting all non-root nodes to every + other non-root node. Edges with an arc > pi/2 around root are discarded + The length of each edge is the euclidean distance between its vertices.''' + M = G_base.graph['M'] + VertexC = G_base.graph['VertexC'] + N = VertexC.shape[0] - M + NodeC = VertexC[:-M] + RootC = VertexC[-M:] + Root = range(-M, 0) + V = N + (M if include_roots else 0) + G = nx.complete_graph(V) + EdgeComplete = np.column_stack(np.triu_indices(V, k=1)) + # mask = np.zeros((V,), dtype=bool) + mask = np.zeros_like(EdgeComplete[0], dtype=bool) + if include_roots: + # mask root-root edges + offset = 0 + for i in range(0, M - 1): + for j in range(0, M - i - 1): + mask[offset + j] = True + offset += (V - i - 1) + + # make node indices span -M:(N - 1) + EdgeComplete -= M + nx.relabel_nodes(G, dict(zip(range(N, N + M), Root)), + copy=False) + C = cdist(VertexC, VertexC) + else: + C = cdist(NodeC, NodeC) + if prune: + # prune edges that cover more than 90° angle from any root + SrcC = VertexC[EdgeComplete[:, 0]] + DstC = VertexC[EdgeComplete[:, 1]] + for root in Root: + rootC = VertexC[root] + # calculates the dot product of vectors representing the + # nodes of each edge wrt root; then mark the negative ones + # (angle > pi/2) on `mask` + mask |= ((SrcC - rootC) * (DstC - rootC)).sum(axis=1) < 0 + Edge = EdgeComplete[~mask] + # discard masked edges + G.remove_edges_from(EdgeComplete[mask]) + if crossings: + # get_crossings_map() takes time and space + G.graph['crossings'] = get_crossings_map(Edge, VertexC) + # assign nodes to roots? + # remove edges between nodes belonging to distinct roots whose length is + # greater than both d2root + G.graph.update(G_base.graph) + nx.set_node_attributes(G, G_base.nodes) + for u, v, edgeD in G.edges(data=True): + edgeD['length'] = C[u, v] + # assign the edge to the root closest to the edge's middle point + edgeD['root'] = -M + np.argmin( + cdist(((VertexC[u] + VertexC[v]) / 2)[np.newaxis, :], RootC)) + return G + + +def A_graph(G_base, delaunay_based=True, weightfun=None, weight_attr='weight'): + ''' + Return the "available edges" graph that is the base for edge search in + Esau-Williams. If `delaunay_based` is True, the edges are the expanded + Delaunay triangulation, otherwise a complete graph is returned. + + This function is being kept for backward-compatibility. For the Delaunay + triangulation, call `delaunay()` directly. + ''' + if delaunay_based: + A = delaunay(G_base) + if weightfun is not None: + apply_edge_exemptions(A) + else: + A = complete_graph(G_base, include_roots=True) + # intersections + # I = get_crossings_list(np.array(A.edges()), VertexC) + + if weightfun is not None: + for u, v, data in A.edges(data=True): + data[weight_attr] = weightfun(data) + + # remove all gates from A + # TODO: decide about this line + # A.remove_edges_from(list(A.edges(range(-M, 0)))) + return A + + +def planar_over_layout(G: nx.Graph): + ''' + Return a PlanarEmbedding of a triangulation of the nodes in G, provided + G has been created using the extended Delaunay edges. + + If `G` does not have a `relax_boundary` attribute, it is assumed True. + + The PlanarEmbedding is different from the one generated in `A_graph()` in + that it takes into account the actual edges used in G (i.e. used diagonals + will be included in the planar graph to the exclusion of Delaunay edges + that cross them). + ''' + M = G.graph['M'] + VertexC = G.graph['VertexC'] + BoundaryC = G.graph.get('boundary') + relax_boundary = G.graph.get('relax_boundary', True) + P, diagonals = make_planar_embedding( + M, VertexC, BoundaryC=None if relax_boundary else BoundaryC) + for r in range(-M, 0): + for u, v in nx.edge_dfs(G, r): + # update the planar embedding to include any Delaunay diagonals + # used in G; the corresponding crossing Delaunay edge is removed + u, v = (u, v) if u < v else (v, u) + s = diagonals.get((u, v)) + if s is not None: + t = P[u][s]['ccw'] # same as P[v][s]['cw'] + if (s, t) in G.edges: + # (u, v) & (s, t) are in G (i.e. a crossing). This means + # the diagonal (u, v) is a gate and (s, t) should remain + continue + P.add_half_edge_cw(u, v, t) + P.add_half_edge_cw(v, u, s) + P.remove_edge(s, t) + P.graph['diagonals'] = diagonals + return P + + +def minimum_spanning_tree(G: nx.Graph) -> nx.Graph: + '''Return a graph of the minimum spanning tree connecting the node in G.''' + M = G.graph['M'] + VertexC = G.graph['VertexC'] + V = VertexC.shape[0] + N = V - M + P = make_planar_embedding(M, VertexC)[0].to_undirected(as_view=True) + E_planar = np.array(P.edges, dtype=np.int32) + # E_planar = np.array(P.edges) + Length = np.hypot(*(VertexC[E_planar[:, 0]] - VertexC[E_planar[:, 1]]).T) + E_planar[E_planar < 0] += V + P_ = coo_array((Length, (*E_planar.T,)), shape=(V, V)) + Q_ = scipy_mst(P_) + S, T = Q_.nonzero() + H = nx.Graph() + H.add_nodes_from(G.nodes(data=True)) + for s, t in zip(S, T): + H.add_edge(s if s < N else s - V, t if t < N else t - V, + length=Q_[s, t]) + H.graph.update(G.graph) + return H + + +# TODO: MARGIN is ARBITRARY - depends on the scale +def check_crossings(G, debug=False, MARGIN=0.1): + '''Checks for crossings (touch/overlap is not considered crossing). + This is an independent check on the tree resulting from the heuristic. + It is not supposed to be used within the heuristic. + MARGIN is how far an edge can advance across another one and still not be + considered a crossing.''' + VertexC = G.graph['VertexC'] + M = G.graph['M'] + N = G.number_of_nodes() - M + + D = G.graph.get('D') + if D is not None: + N -= D + # detournodes = range(N, N + D) + # G.add_nodes_from(((s, {'type': 'detour'}) + # for s in detournodes)) + # clone2prime = G.graph['clone2prime'] + # assert len(clone2prime) == D, \ + # 'len(clone2prime) != D' + # fnT = np.arange(N + D + M) + # fnT[N: N + D] = clone2prime + # DetourC = VertexC[clone2prime].copy() + fnT = G.graph['fnT'] + AllnodesC = np.vstack((VertexC[:N], VertexC[fnT[N:N + D]], + VertexC[-M:])) + else: + fnT = np.arange(N + M) + AllnodesC = VertexC + roots = range(-M, 0) + fnT[-M:] = roots + n2s = NodeStr(fnT, N) + + crossings = [] + pivot_plus_edge = [] + + def check_neighbors(neighbors, w, x, pivots): + '''Neighbors is a bunch of nodes, `pivots` is used only for reporting. + (`w`, `x`) is the edge to be checked if it splits neighbors apart. + ''' + maxidx = len(neighbors) - 1 + if maxidx <= 0: + return + ref = neighbors[0] + i = 1 + while point_d2line(*AllnodesC[[ref, w, x]]) < MARGIN: + # ref node is approx. overlapping the edge: get the next one + ref = neighbors[i] + i += 1 + if i > maxidx: + return + + for n2test in neighbors[i:]: + if point_d2line(*AllnodesC[[n2test, w, x]]) < MARGIN: + # cmp node is approx. overlapping the edge: skip + continue + # print(F[fnT[w]], F[fnT[x]], F[fnT[ref]], F[fnT[cmp]]) + if not is_same_side(*AllnodesC[[w, x, ref, n2test]], + touch_is_cross=False): + print(f'ERROR <splitting>: edge {n2s(w, x)} crosses ' + f'{n2s(ref, *pivots, n2test)}') + # crossings.append(((w, x), (ref, pivot, cmp))) + crossings.append(((w, x), (ref, n2test))) + return True + + for root in roots: + # edges = list(nx.edge_dfs(G, source=root)) + edges = list(nx.edge_bfs(G, source=root)) + # outstr = ', '.join([f'«{F[fnT[u]]}–{F[fnT[v]]}»' for u, v in edges]) + # print(outstr) + potential = [] + for i, (u, v) in enumerate(edges): + for s, t in edges[(i + 1):]: + if s == u or s == v: + continue + uvst = np.array((u, v, s, t), dtype=int) + if is_crossing(*AllnodesC[uvst], touch_is_cross=True): + potential.append(uvst) + distances = np.fromiter( + (point_d2line(*AllnodesC[[p, w, x]]) + for p, w, x in ((u, s, t), + (v, s, t), + (s, u, v), + (t, u, v))), + dtype=float, + count=4) + # print('distances[' + + # ', '.join((F[fnT[n]] for n in (u, v, s, t))) + + # ']: ', distances) + nearmask = distances < MARGIN + close_count = sum(nearmask) + # print('close_count =', close_count) + if close_count == 0: + # (u, v) crosses (s, t) away from nodes + crossings.append(((u, v), (s, t))) + # print(distances) + print(f'ERROR <edge-edge>: ' + f'edge «{F[fnT[u]]}–{F[fnT[v]]}» ' + f'crosses «{F[fnT[s]]}–{F[fnT[t]]}»') + elif close_count == 1: + # (u, v) and (s, t) touch node-to-edge + pivotI, = np.flatnonzero(nearmask) + w, x = (u, v) if pivotI > 1 else (s, t) + pivot = uvst[pivotI] + neighbors = list(G[pivot]) + entry = (pivot, w, x) + if (entry not in pivot_plus_edge and + check_neighbors(neighbors, w, x, (pivot,))): + pivot_plus_edge.append(entry) + elif close_count == 2: + # (u, v) and (s, t) touch node-to-node + touch_uv, touch_st = uvst[np.flatnonzero(nearmask)] + free_uv, free_st = uvst[np.flatnonzero(~nearmask)] + # print( + # f'touch/free u, v :«{F[fnT[touch_uv]]}–' + # f'{F[fnT[free_uv]]}»; s, t:«{F[fnT[touch_st]]}–' + # f'{F[fnT[free_st]]}»') + nb_uv, nb_st = list(G[touch_uv]), list(G[touch_st]) + # print([F[fnT[n]] for n in nb_uv]) + # print([F[fnT[n]] for n in nb_st]) + nbNuv, nbNst = len(nb_uv), len(nb_st) + if nbNuv == 1 or nbNst == 1: + # <a leaf node with a clone – not a crossing> + continue + elif nbNuv == 2: + crossing = is_bunch_split_by_corner( + AllnodesC[nb_st], + *AllnodesC[[nb_uv[0], touch_uv, nb_uv[1]]], + margin=MARGIN)[0] + elif nbNst == 2: + crossing = is_bunch_split_by_corner( + AllnodesC[nb_uv], + *AllnodesC[[nb_st[0], touch_st, nb_st[1]]], + margin=MARGIN)[0] + else: + print('UNEXPECTED case!!! Look into it!') + # mark as crossing just to make sure it is noticed + crossing = True + if crossing: + print(f'ERROR <split>: edges ' + f'«{F[fnT[u]]}–{F[fnT[v]]}» ' + f'and «{F[fnT[s]]}–{F[fnT[t]]}» ' + f'break a bunch apart at ' + f'{F[fnT[touch_uv]]}, {F[fnT[touch_st]]}') + crossings.append(((u, v), (s, t))) + else: # close_count > 2: + # segments (u, v) and (s, t) are almost parallel + # find the two nodes furthest apart + pairs = np.array(((u, v), (u, s), (u, t), + (s, t), (v, t), (v, s))) + furthest = np.argmax( + np.hypot(*(AllnodesC[pairs[:, 0]] - + AllnodesC[pairs[:, 1]]).T)) + # print('furthest =', furthest) + w, x = pairs[furthest] + q, r = pairs[furthest - 3] + if furthest % 3 == 0: + # (q, r) is contained within (w, x) + neighbors = list(G[q]) + list(G[r]) + neighbors.remove(q) + neighbors.remove(r) + check_neighbors(neighbors, w, x, (q, r)) + else: + # (u, v) partially overlaps (s, t) + neighbors_q = list(G[q]) + neighbors_q.remove(w) + check_neighbors(neighbors_q, s, t, (q,)) + # print(crossings) + neighbors_r = list(G[r]) + neighbors_r.remove(x) + check_neighbors(neighbors_r, u, v, (r,)) + # print(crossings) + if neighbors_q and neighbors_r: + for a, b in product(neighbors_q, neighbors_r): + if is_same_side(*AllnodesC[[q, r, a, b]]): + print(f'ERROR <partial ovelap>: edge ' + f'«{F[fnT[u]]}–{F[fnT[v]]}» ' + f'crosses ' + f'«{F[fnT[s]]}–{F[fnT[t]]}»') + crossings.append(((u, v), (s, t))) + debug and potential and print( + 'potential crossings: ' + + ', '.join([f'«{F[fnT[u]]}–{F[fnT[v]]}» × «{F[fnT[s]]}–{F[fnT[t]]}»' + for u, v, s, t in potential])) + return crossings + + +def rotating_calipers(convex_hull: np.ndarray) \ + -> Tuple[np.ndarray, float, float, np.ndarray]: + # inspired by: + # jhultman/rotating-calipers: + # CUDA and Numba implementations of computational geometry algorithms. + # (https://github.com/jhultman/rotating-calipers) + """ + argument `convex_hull` is a (N, 2) array of coordinates of the convex hull + in counter-clockwise order. + Reference: + Toussaint, Godfried T. "Solving geometric problems with + the rotating calipers." Proc. IEEE Melecon. Vol. 83. 1983. + """ + caliper_angles = np.float_([0.5 * np.pi, 0, -0.5 * np.pi, np.pi]) + area_min = np.inf + N = convex_hull.shape[0] + left, bottom = convex_hull.argmin(axis=0) + right, top = convex_hull.argmax(axis=0) + + calipers = np.int_([left, top, right, bottom]) + + for _ in range(N): + # Roll vertices counter-clockwise + calipers_advanced = (calipers - 1) % N + # Vectors from previous calipers to candidates + vec = convex_hull[calipers_advanced] - convex_hull[calipers] + # Find angles of candidate edgelines + angles = np.arctan2(vec[:, 1], vec[:, 0]) + # Find candidate angle deltas + angle_deltas = caliper_angles - angles + # Select pivot with smallest rotation + pivot = np.abs(angle_deltas).argmin() + # Advance selected pivot caliper + calipers[pivot] = calipers_advanced[pivot] + # Rotate all supporting lines by angle delta + caliper_angles -= angle_deltas[pivot] + + # calculate area for current calipers + angle = caliper_angles[np.abs(caliper_angles).argmin()] + c, s = np.cos(angle), np.sin(angle) + calipers_rot = convex_hull[calipers] @ np.array(((c, -s), (s, c))) + bbox_rot_min = calipers_rot.min(axis=0) + bbox_rot_max = calipers_rot.max(axis=0) + area = (bbox_rot_max - bbox_rot_min).prod() + # check if area is a new minimum + if area < area_min: + area_min = area + best_calipers = calipers.copy() + best_caliper_angle = angle + best_bbox_rot_min = bbox_rot_min + best_bbox_rot_max = bbox_rot_max + + c, s = np.cos(-best_caliper_angle), np.sin(-best_caliper_angle) + t = best_bbox_rot_max + b = best_bbox_rot_min + # calculate bbox coordinates in original reference frame, ccw vertices + bbox = np.float_(((b[0], b[1]), + (b[0], t[1]), + (t[0], t[1]), + (t[0], b[1]))) @ np.array(((c, -s), (s, c))) + + return best_calipers, best_caliper_angle, area_min, bbox diff --git a/ed_win/plotting_scripts/heuristics.py b/ed_win/plotting_scripts/heuristics.py new file mode 100644 index 0000000..7ccdfe2 --- /dev/null +++ b/ed_win/plotting_scripts/heuristics.py @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +from .CrossingPreventingEW import CPEW +from .NonBranchingEW import NBEW +from .ObstacleBypassingEW import OBEW +from .ClassicEsauWilliams import ClassicEW diff --git a/ed_win/plotting_scripts/importer.py b/ed_win/plotting_scripts/importer.py new file mode 100644 index 0000000..df6cf6c --- /dev/null +++ b/ed_win/plotting_scripts/importer.py @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import os +from collections import namedtuple +from pathlib import Path +from typing import NamedTuple +from importlib.resources import files + +import networkx as nx +import numpy as np +import utm +import yaml + +from .geometric import make_graph_metrics +from .utils import NodeTagger + +F = NodeTagger() + + +def translate_latlonstr(entries): + ''' + This requires a very specific string format: + TAG 11°22.333'N 44°55.666'E + (no spaces within one coordinate). + ''' + translated = [] + for entry in entries.splitlines(): + tag, lat, lon = entry.split(' ') + latlon = [] + for ll in (lat, lon): + val, hemisphere = ll.split("'") + deg, sec = val.split('°') + latlon.append((float(deg) + float(sec) / 60) * + (1 if hemisphere in ('N', 'E') else -1)) + translated.append((tag, *utm.from_latlon(*latlon))) + return translated + + +def _tags_and_array_from_key(key, parsed_dict): + # separate data into columns + tags, eastings, northings, zone_numbers, zone_letters = \ + zip(*translate_latlonstr(parsed_dict[key])) + # all coordinates must belong to the same UTM zone + assert all(num == zone_numbers[0] for num in zone_numbers[1:]) + assert all(letter == zone_letters[0] for letter in zone_letters[1:]) + return np.c_[eastings, northings], tags + + +def graph_from_yaml(filepath, handle=None) -> nx.Graph: + '''Import wind farm data from .yaml file.''' + fpath = filepath.with_suffix('.yaml') + # read wind power plant site YAML file + parsed_dict = yaml.safe_load(open(fpath, 'r', encoding='utf8')) + Boundary, BoundaryTag = _tags_and_array_from_key('EXTENTS', parsed_dict) + Root, RootTag = _tags_and_array_from_key('SUBSTATIONS', parsed_dict) + Node, NodeTag = _tags_and_array_from_key('TURBINES', parsed_dict) + + # create networkx graph + N = Node.shape[0] + M = Root.shape[0] + G = nx.Graph(M=M, + VertexC=np.vstack([Node, Root]), + boundary=Boundary, + name=fpath.stem, + handle=handle) + lsangle = parsed_dict.get('LANDSCAPE_ANGLE') + if lsangle is not None: + G.graph['landscape_angle'] = lsangle + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg', 'tag': NodeTag[n]}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss', 'tag': RootTag[r]}) + for r in range(-M, 0))) + make_graph_metrics(G) + return G + + +_site_handles = dict( + anholt='Anholt', + borkum='Borkum Riffgrund 1', + borkum2='Borkum Riffgrund 2', + borssele='Borssele', + dantysk='DanTysk', + anglia='East Anglia ONE', + gode='Gode Wind 1', + gabbin='Greater Gabbard Inner', + gwynt='Gwynt y Mor', + hornsea='Hornsea One', + hornsea2w='Hornsea Two West', + horns='Horns Rev 1', + horns2='Horns Rev 2', + horns3='Horns Rev 3', + london='London Array', + moray='Moray East', + ormonde='Ormonde', + race='Race Bank', + rampion='Rampion', + rødsand2='Rødsand 2', + thanet='Thanet', + triton='Triton Knoll', + sands='West of Duddon Sands', +) + + +def load_repository(handles2name=_site_handles) -> NamedTuple: + base_dir = files('interarray.data') + return namedtuple('SiteRepository', handles2name)( + *(graph_from_yaml(base_dir / fname, handle) + for handle, fname in handles2name.items())) diff --git a/ed_win/plotting_scripts/interarraylib.py b/ed_win/plotting_scripts/interarraylib.py new file mode 100644 index 0000000..f2a52f0 --- /dev/null +++ b/ed_win/plotting_scripts/interarraylib.py @@ -0,0 +1,284 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import pickle +import sys +from hashlib import sha256 +from typing import Any, Dict, Tuple + +import networkx as nx +import numpy as np + +from .utils import NodeTagger +from .geometric import make_graph_metrics + +F = NodeTagger() + + +def new_graph_like(G_base, edges=None): + '''copies graph and nodes attributes, but not edges''' + G = nx.Graph() + G.graph.update(G_base.graph) + G.add_nodes_from(G_base.nodes(data=True)) + if edges: + G.add_edges_from(edges) + return G + + +def G_base_from_G(G: nx.Graph) -> nx.Graph: + ''' + Return new graph with nodes (including label and type) and boundary of G. + In addition, output graph has metrics. + + Similar to `new_graph_like()`, but works with layout solutions that carry + a lot of extra info (which it discards). + ''' + M = G.graph['M'] + N = G.graph['VertexC'].shape[0] - M + transfer_fields = ('name', 'handle', 'VertexC', 'M', 'boundary', + 'landscape_angle') + G_base = nx.Graph(**{k: G.graph[k] for k in transfer_fields}) + G_base.add_nodes_from(((n, {'label': label}) + for n, label in G.nodes(data='label') + if 0 <= n < N), type='wtg') + for r in range(-M, 0): + G_base.add_node(r, label=G.nodes[r]['label'], type='oss') + make_graph_metrics(G_base) + return G_base + + +def G_from_site(*, VertexC: np.ndarray, boundary: np.ndarray, M: int, + **kwargs) -> nx.Graph: + ''' + Arguments: + - 'VertexC': numpy.ndarray (V, 2) with x, y pos. of wtg + oss (total V) + - 'boundary': numpy.ndarray (P, 2) with x, y pos. of P polygon vertices + - 'M': int number of oss + + Addtional relevant arguments: + - 'name': str site name + - 'handle': str site identifier + - 'relax_boundary: boolean if True allows edges to cross boundary + + Returns: `networkx.Graph` containing V nodes and no edges. All keyword + arguments are made available in `Graph().graph` dict. + ''' + N = len(VertexC) - M + if 'handle' not in kwargs: + kwargs['handle'] = 'G_from_site' + if 'name' not in kwargs: + kwargs['name'] = kwargs['handle'] + G = nx.Graph(M=M, + VertexC=VertexC, + boundary=boundary, + **kwargs) + + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + return G + + +def G_from_T(T, G_base, capacity=None, cost_scale=1e3): + '''Creates a networkx graph with nodes and data from G_base and edges from + a T matrix. (suitable for converting the output of juru's `global_optimizer`) + T matrix: [ [u, v, length, cable type, load (WT number), cost] ]''' + G = nx.Graph() + G.graph.update(G_base.graph) + G.add_nodes_from(G_base.nodes(data=True)) + M = G_base.graph['M'] + N = G_base.number_of_nodes() - M + + # indexing differences: + # T starts at 1, while G starts at -M + edges = (T[:, :2].astype(int) - M - 1) + + G.add_edges_from(edges) + nx.set_edge_attributes( + G, {(int(u), int(v)): dict(length=length, cable=cable, load=load, cost=cost) + for (u, v), length, (cable, load), cost in + zip(edges, T[:, 2], T[:, 3:5].astype(int), cost_scale * T[:, 5])}) + G.graph['has_loads'] = True + G.graph['has_costs'] = True + G.graph['edges_created_by'] = 'G_from_T()' + if capacity is not None: + G.graph['capacity'] = capacity + G.graph['overfed'] = [len(G[root]) / np.ceil(N / capacity) * M + for root in range(-M, 0)] + return G + + +def G_from_TG(T, G_base, capacity=None, load_col=4): + ''' + DEPRECATED in favor of `G_from_T()` + + Creates a networkx graph with nodes and data from G_base and edges from + a T matrix. + T matrix: [ [u, v, length, load (WT number), cable type], ...] + ''' + G = nx.Graph() + G.graph.update(G_base.graph) + G.add_nodes_from(G_base.nodes(data=True)) + M = G_base.graph['M'] + N = G_base.number_of_nodes() - M + + # indexing differences: + # T starts at 1, while G starts at 0 + # T begins with OSSs followed by WTGs, + # while G begins with WTGs followed by OSSs + # the line bellow converts the indexing: + edges = (T[:, :2].astype(int) - M - 1) % (N + M) + + G.add_weighted_edges_from(zip(*edges.T, T[:, 2]), weight='length') + # nx.set_edge_attributes(G, {(u, v): load for (u, v), load + # in zip(edges, T[:, load_col])}, + # name='load') + # try: + calcload(G) + # except AssertionError as err: + # print(f'>>>>>>>> SOMETHING WENT REALLY WRONG: {err} <<<<<<<<<<<') + # return G + if T.shape[1] >= 4: + for (u, v), load in zip(edges, T[:, load_col]): + Gload = G.edges[u, v]['load'] + assert Gload == load, ( + f'<G.edges[{u}, {v}]> {Gload} != {load} <T matrix>') + G.graph['has_loads'] = True + G.graph['edges_created_by'] = 'G_from_TG()' + G.graph['prevented_crossings'] = 0 + if capacity is not None: + G.graph['overfed'] = [len(G[root]) / np.ceil(N / capacity) * M + for root in range(N, N + M)] + return G + + +def update_lengths(G): + '''Adds missing edge lengths. + Changes G in place.''' + VertexC = G.graph['VertexC'] + for u, v, dataE in G.edges.data(): + if 'length' not in dataE: + dataE['length'] = np.hypot(*(VertexC[u] - VertexC[v]).T) + + +def bfs_subtree_loads(G, parent, children, subtree): + ''' + Recurse down the subtree, updating edge and node attributes. Return value + is total descendant nodes. Meant to be called by `calcload()`, but can be + used independently (e.g. from PathFinder). + Nodes must not have a 'load' attribute. + ''' + nodeD = G.nodes[parent] + default = 1 if nodeD['type'] == 'wtg' else 0 + if not children: + nodeD['load'] = default + return default + load = nodeD.get('load', default) + for child in children: + G.nodes[child]['subtree'] = subtree + grandchildren = set(G[child].keys()) + grandchildren.remove(parent) + childload = bfs_subtree_loads(G, child, grandchildren, subtree) + G[parent][child].update(( + ('load', childload), + ('reverse', parent > child) + )) + load += childload + nodeD['load'] = load + return load + + +def calcload(G): + ''' + Perform a breadth-first-traversal of each root's subtree. As each node is + visited, its subtree id and the load leaving it are stored as its + attribute (keys 'subtree' and 'load', respectively). Also the edges' + 'load' attributes are updated accordingly. + ''' + M = G.graph['M'] + N = G.number_of_nodes() - M + D = G.graph.get('D') + if D is not None: + N -= D + roots = range(-M, 0) + for node, data in G.nodes(data=True): + if 'load' in data: + del data['load'] + + subtree = 0 + total_load = 0 + for root in roots: + G.nodes[root]['load'] = 0 + for subroot in G[root]: + bfs_subtree_loads(G, root, [subroot], subtree) + subtree += 1 + total_load += G.nodes[root]['load'] + assert total_load == N, f'counted ({total_load}) != nonrootnodes({N})' + G.graph['has_loads'] = True + + +def pathdist(G, path): + ''' + Return the total length (distance) of a `path` of nodes in `G` from nodes' + coordinates (does not rely on edge attributes). + ''' + VertexC = G.graph['VertexC'] + dist = 0. + p = path[0] + for n in path[1:]: + dist += np.hypot(*(VertexC[p] - VertexC[n]).T) + p = n + return dist + + +def remove_detours(H: nx.Graph) -> nx.Graph: + ''' + Create a shallow copy of `H` without detour nodes + (and *with* the resulting crossings). + ''' + G = H.copy() + M = G.graph['M'] + VertexC = G.graph['VertexC'] + N = G.number_of_nodes() - M - G.graph.get('D', 0) + for r in range(-M, 0): + detoured = [n for n in G.neighbors(r) if n >= N] + if detoured: + G.graph['crossings'] = [] + for n in detoured: + ref = r + G.remove_edge(n, r) + while n >= N: + ref = n + n, = G.neighbors(ref) + G.remove_node(ref) + G.add_edge(n, r, + load=G.nodes[n]['load'], + reverse=False, + length=np.hypot(*(VertexC[n] - VertexC[r]).T)) + G.graph['crossings'].append((r, n)) + G.graph.pop('D', None) + G.graph.pop('fnT', None) + return G + + +def site_fingerprint(VertexC: np.ndarray, boundary: np.ndarray) \ + -> Tuple[bytes, Dict[str, bytes]]: + # VertexCpkl = pickle.dumps(np.round(VertexC, 2)) + # boundarypkl = pickle.dumps(np.round(boundary, 2)) + VertexCpkl = pickle.dumps(VertexC) + boundarypkl = pickle.dumps(boundary) + return (sha256(VertexCpkl + boundarypkl).digest(), + dict(VertexC=VertexCpkl, boundary=boundarypkl)) + + +def fun_fingerprint(fun=None) -> Dict[str, Any]: + if fun is None: + fcode = sys._getframe().f_back.f_code + else: + fcode = fun.__code__ + return dict( + funhash=sha256(fcode.co_code).digest(), + funfile=fcode.co_filename, + funname=fcode.co_name, + ) diff --git a/ed_win/plotting_scripts/interface.py b/ed_win/plotting_scripts/interface.py new file mode 100644 index 0000000..494c957 --- /dev/null +++ b/ed_win/plotting_scripts/interface.py @@ -0,0 +1,213 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import numpy as np +import numpy.lib.recfunctions as nprec +import networkx as nx +from functools import partial + +from .heuristics import CPEW, NBEW, OBEW, ClassicEW +from .interarraylib import calcload, F +from .geometric import make_graph_metrics + +heuristics = { + 'CPEW': CPEW, + 'NBEW': NBEW, + 'OBEW': OBEW, + 'OBEW_0.6': partial(OBEW, rootlust='0.6*cur_capacity/capacity'), +} + + +def translate2global_optimizer(G): + VertexC = G.graph['VertexC'] + M = G.graph['M'] + X, Y = np.hstack((VertexC[-1:-1 - M:-1].T, VertexC[:-M].T)) + return dict(WTc=G.number_of_nodes() - M, OSSc=M, X=X, Y=Y) + + +def assign_cables(G, cables): + ''' + G: networkx graph with edges having a 'load' attribute (use calcload(G)) + cables: [(«cross section», «capacity», «cost»), ...] in increasing + capacity order (each cable entry must be a tuple) + or numpy.ndarray where each row represents one cable type + + The attribute 'cost' of the edges of G will be updated with their cost, + considering the cheapest cable that can handle their load. + A new attribute 'cable' will be assigned to each edge with the index of the + cable chosen. + ''' + + Nc = len(cables) + dt = np.dtype([('area', float), + ('capacity', int), + ('cost', float)]) + if isinstance(cables, np.ndarray): + cable_ = nprec.unstructured_to_structured(cables, dtype=dt) + else: + cable_ = np.fromiter(cables, dtype=dt, count=Nc) + κ_ = cable_['capacity'] + capacity = 1 + + # for e, data in G.edges.items(): + for u, v, data in G.edges(data=True): + i = κ_.searchsorted(data['load']) + if i >= len(κ_): + print(f'ERROR: Load for edge ⟨{u, v}⟩: {data["load"]} ' + f'exceeds maximum cable capacity {κ_[-1]}.') + data['cable'] = i + data['cost'] = data['length'] * cable_['cost'][i] + if data['load'] > capacity: + capacity = data['load'] + G.graph['cables'] = cable_ + G.graph['has_costs'] = True + if 'capacity' not in G.graph: + G.graph['capacity'] = capacity + + +def assign_subtree(G): + start = 0 + queue = [] + for root in range(-G.graph['M'], 0): + for subtree, gate in enumerate(G[root], start=start): + queue.append((root, gate)) + while queue: + parent, node = queue.pop() + G.nodes[node]['subtree'] = subtree + for nbr in G[node]: + if nbr != parent: + queue.append((node, nbr)) + start = subtree + 1 + + +def G_from_XYM(X, Y, M=1, name='unnamed', boundary=None): + ''' + This function assumes that the first M vertices are OSSs + X: x coordinates of vertices + Y: y coordinates of vertices + M: number of OSSs + ''' + assert len(X) == len(Y), 'ERROR: X and Y lengths must match' + N = len(X) - M + + # create networkx graph + G = nx.Graph(M=M, + VertexC=np.r_[ + np.c_[X[M:], Y[M:]], + np.c_[X[M - 1::-1], Y[M - 1::-1]] + ], + name=name) + if boundary is None: + G.graph['boundary'] = np.array(( + (min(X), min(Y)), + (min(X), max(Y)), + (max(X), max(Y)), + (max(X), min(Y)))) + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + make_graph_metrics(G) + return G + + +def T_from_G(G): + ''' + G: networkx graph + + returns: + T: [ («u», «v», «length», «load (WT number)», «cable type», «edge cost»), ...] + + (T is a numpy record array) + ''' + M = G.graph['M'] + Ne = G.number_of_edges() + + def edge_parser(edges): + for u, v, data in edges: + # OSS index starts at 0 + # u = (u + M) if u > 0 else abs(u) - 1 + # v = (v + M) if v > 0 else abs(v) - 1 + # OSS index starts at 1 + s = (u + M + 1) if u >= 0 else abs(u) + t = (v + M + 1) if v >= 0 else abs(v) + # print(u, v, '->', s, t) + yield (s, t, data['length'], data['load'], data['cable'], data['cost']) + + T = np.fromiter(edge_parser(G.edges(data=True)), + dtype=[('u', int), + ('v', int), + ('length', float), + ('load', int), + ('cable', int), + ('cost', float)], + count=Ne) + return T + + +class HeuristicFactory(): + ''' + Initializes a heuristic algorithm. + Inputs: + N: number of nodes + M: number of roots + rootC: 2D nympy array (M, 2) of the XY coordinates of the roots + boundaryC: 2D numpy array (_, 2) of the XY coordinates of the boundary + cables: [(«cross section», «capacity», «cost»), ...] in increasing capacity order + name: site name + ''' + + def __init__(self, N, M, rootC, boundaryC, heuristic, cables, name='unnamed'): + self.N = N + self.M = M + self.cables = cables + self.k = cables[-1][1] + self.VertexC = np.empty((N + M, 2), dtype=float) + self.VertexC[N:] = rootC + # create networkx graph + self.G_base = nx.Graph(M=M, + VertexC=self.VertexC, + boundary=boundaryC, + name=name) + self.G_base.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + self.G_base.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + self.heuristic = heuristics[heuristic] + + def calccost(self, X, Y): + assert len(X) == len(Y) == self.N + self.VertexC[:self.N, 0] = X + self.VertexC[:self.N, 1] = Y + make_graph_metrics(self.G_base) + self.G = self.heuristic(self.G_base, capacity=self.k) + calcload(self.G) + assign_cables(self.G, self.cables) + return self.G.size(weight='cost') + + def get_table(self): + ''' + Must have called cost() at least once. Only the last call's layout is available. + returns: + T: [ («u», «v», «length», «load (WT number)», «cable type», «edge cost»), ...] + ''' + return T_from_G(self.G) + + +def heuristic_wrapper(X, Y, cables, M=1, heuristic='CPEW', return_graph=False): + ''' + This function assumes that the first M vertices are OSSs + X: x coordinates of vertices + Y: y coordinates of vertices + cables: [(«cross section», «capacity», «cost»), ...] in increasing capacity order + M: number of OSSs + heuristic: {'CPEW', 'OBEW'} + ''' + G_base = G_from_XYM(X, Y, M) + G = heuristics[heuristic](G_base, capacity=cables[-1][1]) + calcload(G) + assign_cables(G, cables) + if return_graph: + return T_from_G(G), G + else: + return T_from_G(G) diff --git a/ed_win/plotting_scripts/new_dbmodel.py b/ed_win/plotting_scripts/new_dbmodel.py new file mode 100644 index 0000000..840e082 --- /dev/null +++ b/ed_win/plotting_scripts/new_dbmodel.py @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import datetime +import os + +from pony.orm import (Database, IntArray, Json, Optional, PrimaryKey, Required, + Set) + + +def open_database(filename, create_db=False): + db = Database() + define_entities(db) + db.bind('sqlite', os.path.abspath(os.path.expanduser(filename)), + create_db=create_db) + db.generate_mapping(create_tables=True) + return db + + +def define_entities(db): + ''' + Database model for storage of layouts. + Tables: + - NodeSet: site + - EdgeSet: layout + - Method: info on algorithm & options to produce layouts + - Machine: info on machine that generated a layout + ''' + + class NodeSet(db.Entity): + # hashlib.sha256(VertexC + boundary).digest() + name = Required(str, unique=True) + N = Required(int) # # of non-root nodes + M = Required(int) # # of root nodes + # vertices (nodes + roots) coordinates (UTM) + # pickle.dumps(np.empty((N + M, 2), dtype=float) + VertexC = Required(bytes) + # region polygon: P vertices (x, y), ordered ccw + # pickle.dumps(np.empty((P, 2), dtype=float) + boundary = Required(bytes) + landscape_angle = Optional(float) + digest = PrimaryKey(bytes) + EdgeSets = Set(lambda: EdgeSet) + + class EdgeSet(db.Entity): + handle = Required(str) + capacity = Required(int) + length = Required(float) + # runtime always in [s] + runtime = Optional(float) + machine = Optional(lambda: Machine) + gates = Required(IntArray) + N = Required(int) + M = Required(int) + # number of Detour nodes + D = Optional(int, default=0) + timestamp = Optional(datetime.datetime, + default=datetime.datetime.utcnow) + misc = Optional(Json) + clone2prime = Optional(IntArray) + edges = Required(IntArray) + nodes = Required(NodeSet) + method = Required(lambda: Method) + + class Method(db.Entity): + funname = Required(str) + # options is a dict of function parameters + options = Required(Json) + timestamp = Required(datetime.datetime, + default=datetime.datetime.utcnow) + funfile = Required(str) + # hashlib.sha256(fun.__code__.co_code) + funhash = Required(bytes) + # hashlib.sha256(funhash + pickle(options)).digest() + digest = PrimaryKey(bytes) + EdgeSets = Set(EdgeSet) + + class Machine(db.Entity): + name = Required(str, unique=True) + attrs = Optional(Json) + EdgeSets = Set(EdgeSet) + + # class CableSet(db.Entity): + # name = Required(str) + # cableset = Required(bytes) + # EdgeSets = Set(EdgeSet) + # max_capacity = Required(int) + # # name = Required(str) + # # types = Required(int) + # # areas = Required(IntArray) # mm² + # # capacities = Required(IntArray) # num of wtg + # # EdgeSets = Set(EdgeSet) + # # max_capacity = Required(int) diff --git a/ed_win/plotting_scripts/new_storage.py b/ed_win/plotting_scripts/new_storage.py new file mode 100644 index 0000000..648c5a0 --- /dev/null +++ b/ed_win/plotting_scripts/new_storage.py @@ -0,0 +1,285 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import json +import pickle +from collections.abc import Sequence +from hashlib import sha256 +import base64 +from socket import getfqdn, gethostname +from typing import Any, List, Mapping, Optional, Tuple, Union + +import networkx as nx +import numpy as np +from pony import orm + +from .interarraylib import calcload, site_fingerprint +from .utils import NodeTagger + +# Coordinates use arrays of floats. +# Somehow, nodesets with the same coordinates were getting different digests, +# when the code ran on different computers. +# Rouding to a fixed (small) number of decimal place to fix it. +COORDINATES_DECIMAL_PLACES = 2 + +F = NodeTagger() + +PackType = Mapping[str, Any] + + +def base_graph_from_nodeset(nodeset: object) -> nx.Graph: + '''Create the networkx Graph (nodes only) for a given nodeset.''' + N = nodeset.N + M = nodeset.M + G = nx.Graph(name=nodeset.name, + M=M, + VertexC=pickle.loads(nodeset.VertexC), + boundary=pickle.loads(nodeset.boundary), + landscape_angle=nodeset.landscape_angle, + ) + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + return G + + +def graph_from_edgeset(edgeset: object) -> nx.Graph: + nodeset = edgeset.nodes + VertexC = pickle.loads(nodeset.VertexC) + N = nodeset.N + M = nodeset.M + G = nx.Graph(name=nodeset.name, + handle=edgeset.handle, + M=M, + VertexC=VertexC, + capacity=edgeset.capacity, + boundary=pickle.loads(nodeset.boundary), + landscape_angle=nodeset.landscape_angle, + funhash=edgeset.method.funhash, + funfile=edgeset.method.funfile, + funname=edgeset.method.funname, + creation_options=edgeset.method.options, + **edgeset.misc) + + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + + D = edgeset.D or 0 + if D > 0: + G.graph['D'] = D + detournodes = range(N, N + D) + G.add_nodes_from(((s, {'type': 'detour'}) + for s in detournodes)) + clone2prime = edgeset.clone2prime + assert len(clone2prime) == D, \ + 'len(EdgeSet.clone2prime) != EdgeSet.D' + fnT = np.arange(N + D + M) + fnT[N: N + D] = clone2prime + fnT[-M:] = range(-M, 0) + G.graph['fnT'] = fnT + AllnodesC = np.vstack((VertexC[:N], + VertexC[clone2prime], + VertexC[-M:])) + else: + AllnodesC = VertexC + + Length = np.hypot(*(AllnodesC[:-M] - AllnodesC[edgeset.edges]).T) + G.add_weighted_edges_from(zip(range(N + D), edgeset.edges, Length), + weight='length') + if D > 0: + for _, _, edgeD in G.edges(detournodes, data=True): + edgeD['type'] = 'detour' + G.graph['overfed'] = [len(G[root]) / np.ceil(N / edgeset.capacity) * M + for root in range(-M, 0)] + calc_length = G.size(weight='length') + assert abs(calc_length - edgeset.length) < 1, ( + f"recreated graph's total length ({calc_length:.0f}) != " + f"stored total length ({edgeset.length:.0f})") + + # make_graph_metrics(G) + calcload(G) + return G + + +def packnodes(G: nx.Graph) -> PackType: + M = G.graph['M'] + D = G.graph.get('D', 0) + N = G.number_of_nodes() - D - M + digest, pickled_coordinates = site_fingerprint(G.graph['VertexC'], + G.graph['boundary']) + if G.name[0] == '!': + name = G.name + base64.b64encode(digest).decode('ascii') + else: + name = G.name + pack = dict( + digest=digest, + name=name, + N=N, + M=M, + landscape_angle=G.graph.get('landscape_angle', 0.), + **pickled_coordinates, + ) + return pack + + +def packmethod(ffprint: dict, options: Optional[Mapping] = None) -> PackType: + options = options or {} + if 'capacity' in options: + del options['capacity'] + # funhash = sha256(fun.__code__.co_code).digest() + funhash = ffprint['funhash'] + optionsstr = json.dumps(options) + digest = sha256(funhash + optionsstr.encode()).digest() + pack = dict( + digest=digest, + options=options, + **ffprint, + ) + return pack + + +def add_if_absent(entity: object, pack: PackType) -> bytes: + digest = pack['digest'] + with orm.db_session: + if not entity.exists(digest=digest): + entity(**pack) + return digest + + +def method_from_graph(G: nx.Graph, db: orm.Database) -> bytes: + '''Returns primary key of the entry.''' + pack = packmethod(G.graph['fun_fingerprint'], G.graph['creation_options']) + return add_if_absent(db.Method, pack) + + +def nodeset_from_graph(G: nx.Graph, db: orm.Database) -> bytes: + '''Returns primary key of the entry.''' + pack = packnodes(G) + return add_if_absent(db.NodeSet, pack) + + +def edgeset_from_graph(G: nx.Graph, db: orm.Database) -> int: + '''Add a new EdgeSet entry in the database, using the data in `G`. + If the NodeSet or Method are not yet in the database, they will be added. + + Return value: primary key of the newly created EdgeSet record + ''' + misc_not = {'VertexC', 'anglesYhp', 'anglesXhp', 'anglesRank', 'angles', + 'd2rootsRank', 'd2roots', 'name', 'boundary', 'capacity', + 'runtime', 'runtime_unit', 'edges_fun', 'D', 'DetourC', 'fnT', + 'landscape_angle', 'Root', 'creation_options', + 'Subtree', 'funfile', 'funhash', 'funname', 'diagonals', + 'planar', 'has_loads', 'M', 'gates_not_in_A', 'overfed', + 'gnT', 'fun_fingerprint', 'handle', 'hull'} + nodesetID = nodeset_from_graph(G, db) + methodID = method_from_graph(G, db), + machineID = get_machine_pk(db) + M = G.graph['M'] + N = G.graph['VertexC'].shape[0] - M + V = G.number_of_nodes() - M + edges = np.empty((V,), dtype=int) + if not G.graph.get('has_loads'): + calcload(G) + for u, v, reverse in G.edges(data='reverse'): + u, v = (u, v) if u < v else (v, u) + i, target = (u, v) if reverse else (v, u) + edges[i] = target + misc = {key: G.graph[key] + for key in G.graph.keys() - misc_not} + print('Storing in `misc`:', *misc.keys()) + for k, v in misc.items(): + if isinstance(v, np.ndarray): + misc[k] = v.tolist() + elif isinstance(v, np.int64): + misc[k] = int(v) + edgepack = dict( + handle=G.graph.get('handle', + G.graph['name'].strip().replace(' ', '_')), + capacity=G.graph['capacity'], + length=G.size(weight='length'), + runtime=G.graph['runtime'], + gates=[len(G[root]) for root in range(-M, 0)], + N=N, + M=M, + misc=misc, + edges=edges, + ) + D = G.graph.get('D') + if D is not None and D > 0: + N = V - D + edgepack['D'] = D + edgepack['clone2prime'] = G.graph['fnT'][N: N + D] + else: + edgepack['D'] = 0 + with orm.db_session: + edgepack.update( + nodes=db.NodeSet[nodesetID], + method=db.Method[methodID], + machine=db.Machine[machineID], + ) + return db.EdgeSet(**edgepack).get_pk() + + +def get_machine_pk(db: orm.Database) -> int: + fqdn = getfqdn() + hostname = gethostname() + if fqdn == 'localhost': + machine = hostname + else: + if hostname.startswith('n-'): + machine = fqdn[len(hostname):] + else: + machine = fqdn + with orm.db_session: + if db.Machine.exists(name=machine): + return db.Machine.get(name=machine).get_pk() + else: + return db.Machine(name=machine).get_pk() + + +def G_by_method(G: nx.Graph, method: object, db: orm.Database) -> nx.Graph: + '''Fetch from the database a layout for `G` by `method`. + `G` must be a layout solution with the necessary info in the G.graph dict. + `method` is a Method. + ''' + farmname = G.name + c = G.graph['capacity'] + es = db.EdgeSet.get(lambda e: + e.nodes.name == farmname and + e.method is method and + e.capacity == c) + Gdb = graph_from_edgeset(es) + calcload(Gdb) + return Gdb + + +def Gs_from_attrs(farm: object, methods: Union[object, Sequence[object]], + capacities: Union[int, Sequence[int]], + db: orm.Database) -> List[Tuple[nx.Graph]]: + ''' + Fetch from the database a list (one per capacity) of tuples (one per + method) of layouts. + `farm` must have the desired NodeSet name in the `name` attribute. + `methods` is a (sequence of) Method instance(s). + `capacities` is a (sequence of) int(s). + ''' + Gs = [] + if not isinstance(methods, Sequence): + methods = (methods,) + if not isinstance(capacities, Sequence): + capacities = (capacities,) + for c in capacities: + Gtuple = tuple( + graph_from_edgeset( + db.EdgeSet.get(lambda e: + e.nodes.name == farm.name and + e.method is m and + e.capacity == c)) + for m in methods) + for G in Gtuple: + calcload(G) + Gs.append(Gtuple) + return Gs diff --git a/ed_win/plotting_scripts/pathfinding.py b/ed_win/plotting_scripts/pathfinding.py new file mode 100644 index 0000000..a6dce16 --- /dev/null +++ b/ed_win/plotting_scripts/pathfinding.py @@ -0,0 +1,1058 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import heapq +import math +from collections import defaultdict, deque, namedtuple +from itertools import chain +from typing import Callable, Optional, Tuple + +import matplotlib +import networkx as nx +import numpy as np +from loguru import logger + +from .crossings import gateXing_iter +from .geometric import planar_over_layout, rotate +from .interarraylib import bfs_subtree_loads +from .utils import NodeStr, NodeTagger +from .plotting import gplot, scaffolded + +trace, debug, info, success, warn, error, critical = ( + logger.trace, logger.debug, logger.info, logger.success, + logger.warning, logger.error, logger.critical) + +F = NodeTagger() + +NULL = np.iinfo(int).min +PseudoNode = namedtuple('PseudoNode', 'node sector parent dist d_hop'.split()) + + +def rotation_checkers_factory(VertexC: np.ndarray) -> Tuple[ + Callable[[int, int, int], bool], + Callable[[int, int, int], bool]]: + + def cw(A: int, B: int, C: int) -> bool: + """return + True: if A->B->C traverses the triangle ABC clockwise + False: otherwise""" + Ax, Ay = VertexC[A] + Bx, By = VertexC[B] + Cx, Cy = VertexC[C] + return (Bx - Ax) * (Cy - Ay) < (By - Ay) * (Cx - Ax) + + def ccw(A: int, B: int, C: int) -> bool: + """return + True: if A->B->C traverses the triangle ABC counter-clockwise + False: otherwise""" + Ax, Ay = VertexC[B] + Bx, By = VertexC[A] + Cx, Cy = VertexC[C] + return (Bx - Ax) * (Cy - Ay) < (By - Ay) * (Cx - Ax) + + return cw, ccw + + +class PathNodes(dict): + '''Helper class to build a tree that uses clones of base nodes + (i.e. where the same base node can appear as more that one node).''' + + def __init__(self): + super().__init__() + self.count = 0 + self.base_from_id = {} + self.ids_from_base_sector = defaultdict(list) + self.last_added = None + + def add(self, _source, sector, parent, dist, d_hop): + if parent not in self: + error('attempted to add an edge in `PathNodes` to nonexistent' + 'parent ({})', parent) + _parent = self.base_from_id[parent] + for prev_id in self.ids_from_base_sector[_source, sector]: + if self[prev_id].parent == parent: + self.last_added = prev_id + return prev_id + id = self.count + self.count += 1 + self[id] = PseudoNode(_source, sector, parent, dist, d_hop) + self.ids_from_base_sector[_source, sector].append(id) + self.base_from_id[id] = _source + debug('pseudoedge «{}->{}» added', F[_source], F[_parent]) + self.last_added = id + return id + + +class PathFinder(): + ''' + Router for gates that don't belong to the PlanarEmbedding of the graph. + Initialize it with a valid layout graph `G` and it will find paths from + all nodes to the nearest root without crossing any used edges. + These paths can be used to replace the existing gates that cross other + edges by gate paths with detours. + + Example: + ======== + + H = PathFinder(G).create_detours() + ''' + + def __init__(self, G, branching=True, + only_if_crossings=True): + M = G.graph['M'] + VertexC = G.graph['VertexC'] + self.VertexC = VertexC + N = VertexC.shape[0] - M + + # this is just for facilitating printing debug messages + allnodes = np.arange(N + M) + allnodes[-M:] = range(-M, 0) + self.n2s = NodeStr(allnodes, N) + + info('BEGIN pathfinding on "{}" (#wtg = {})', G.graph['name'], N) + P = G.graph.get('planar') or planar_over_layout(G) + self.G, self.P, self.M, self.N = G, P, M, N + # sets of gates (one per root) that are not in the planar embedding + nonembed_Gates = tuple( + np.fromiter(set(G.neighbors(r)) - set(P.neighbors(r)), dtype=int) + for r in range(-M, 0)) + self.nonembed_Gates = nonembed_Gates + if G.graph['edges_created_by'][:5] == 'MILP.': + self.branching = G.graph['creation_options']['branching'] + else: + self.branching = branching + Xings = list(gateXing_iter(G, gates=nonembed_Gates)) + self.Xings = Xings + if not Xings and only_if_crossings: + # no crossings, there is no point in pathfinding + info('Graph has no crossings, skipping path-finding ' + '(pass `only_if_crossings=False` to force path-finding).') + return + self._find_paths() + + def get_best_path(self, n): + ''' + `_.get_best_path(«node»)` produces a `tuple(path, dists)`. + `path` contains a sequence of nodes from the original + networx.Graph `G`, from «node» to the closest root. + `dists` contains the lengths of the segments defined by `paths`. + ''' + paths = self.paths + paths_available = tuple((paths[id].dist, id) + for id in self.I_path[n].values()) + if paths_available: + dist, id = min(paths_available) + path = [n] + dists = [] + pseudonode = paths[id] + while id >= 0: + dists.append(pseudonode.d_hop) + id = pseudonode.parent + path.append(paths.base_from_id[id]) + pseudonode = paths[id] + return path, dists + else: + info('Path not found for «{}»', F[n]) + return [], [] + + def _get_sector(self, _node, portal): + # TODO: there is probably a better way to avoid spinning around _node + is_gate = any(_node in Gate for Gate in self.nonembed_Gates) + _node_degree = len(self.G._adj[_node]) + if is_gate and _node_degree == 1: + root = self.G.nodes[_node]['root'] + return (NULL + if _node == portal[0] else + root) + # return self.G.nodes[_node]['root'] + + _opposite = portal[0] if _node == portal[1] else portal[1] + _nbr = self.P[_node][_opposite]['ccw'] + # this `while` loop would in some cases loop forever + # while ((_node, _nbr) not in self.G.edges): + # _nbr = self.P[_node][_nbr]['ccw'] + for _ in range(_node_degree): + if (_node, _nbr) in self.G.edges: + break + _nbr = self.P[_node][_nbr]['ccw'] + return _nbr + + def _rate_wait_add(self, portal, _new, _apex, apex): + I_path = self.I_path + paths = self.paths + d_hop = np.hypot(*(self.VertexC[_apex] - self.VertexC[_new]).T) + pseudoapex = paths[apex] + d_new = pseudoapex.dist + d_hop + new_sector = self._get_sector(_new, portal) + incumbent = I_path[_new].get(new_sector) + is_better = incumbent is None or d_new < paths[incumbent].dist + yield d_new, portal, (_new, _apex), is_better + new = self.paths.add(_new, new_sector, apex, d_new, d_hop) + self.uncharted[portal] = max(self.uncharted[portal] - 1, 0) + # self.uncharted[portal[1], portal[0]] = False + # get incumbent again, as the situation may have changed + incumbent = I_path[_new].get(new_sector) + if incumbent is None or d_new < paths[incumbent].dist: + self.I_path[_new][new_sector] = new + debug('{} added with d_path = {:.2f}', + self.n2s(_new, _apex), d_new) + + def _advance_portal(self, left, right): + G = self.G + P = self.P + while True: + # look for children portals + n = P[left][right]['ccw'] + if n not in P[right] or P[left][n]['ccw'] == right or n < 0: + # (u, v, n) not a new triangle + # is this is the moment to launch a hull crawler? + return + # examine the other two sides of the triangle + next_portals = [] + for (s, t, side) in ((left, n, 1), (n, right, 0)): + st_sorted = (s, t) if s < t else (t, s) + if (st_sorted not in self.portal_set or + G.nodes[s]['subtree'] == G.nodes[t]['subtree']): + # (s, t) is in G or is bounded by a subtree + continue + next_portals.append(((s, t), side)) + try: + # this `pop()` will raise IndexError if we are at a dead-end + first, fside = next_portals.pop() + # use this instead of the if-else-block when done debugging + # yield left, right, ( + # self._portal_iter(*next_portals[0]) + # if next_portals + # else None) + if next_portals: + second, sside = next_portals[0] + trace(f'branching {self.n2s(*first)} and ' + f'{self.n2s(*second)}') + yield (first, fside, + chain(((second, sside, None),), + self._advance_portal(*second))) + else: + trace('{}', self.n2s(*first)) + yield first, fside, None + except IndexError: + # dead-end reached + return + left, right = first + + def _traverse_channel(self, _apex: int, apex: int, _funnel: list, + wedge_end: list, portal_iter: iter): + # variable naming notation: + # for variables that represent a node, they may occur in two versions: + # - _node: the index it contains maps to a coordinate in VertexC + # - node: contais a pseudonode index (i.e. an index in self.paths) + # translation: _node = paths.base_from_id[node] + cw, ccw = rotation_checkers_factory(self.VertexC) + paths = self.paths + + # for next_left, next_right, new_portal_iter in portal_iter: + for portal, side, new_portal_iter in portal_iter: + # print('[tra]') + _new = portal[side] + if new_portal_iter is not None: + # spawn a branched traverser + # print(f'new channel {self.n2s(_apex, *_funnel)} -> ' + # f"{F[_new]} {'RIGHT' if side else 'LEFT '}") + branched_traverser = self._traverse_channel( + _apex, apex, _funnel.copy(), wedge_end.copy(), + new_portal_iter) + self.bifurcation = branched_traverser + + _nearside = _funnel[side] + _farside = _funnel[not side] + test = ccw if side else cw + + # if _nearside == _apex: # debug info + # print(f"{'RIGHT' if side else 'LEFT '} " + # f'nearside({F[_nearside]}) == apex({F[_apex]})') + debug(f"{'RIGHT' if side else 'LEFT '} " + f'new({F[_new]}) ' + f'nearside({F[_nearside]}) ' + f'farside({F[_farside]}) ' + f'apex({F[_apex]}), wedge ends: ' + f'{F[paths.base_from_id[wedge_end[0]]]}, ' + f'{F[paths.base_from_id[wedge_end[1]]]}') + if _nearside == _apex or test(_nearside, _new, _apex): + # not infranear + if test(_farside, _new, _apex): + # ultrafar (new overlaps farside) + debug('ultrafar') + current_wapex = wedge_end[not side] + _current_wapex = paths.base_from_id[current_wapex] + _funnel[not side] = _current_wapex + contender_wapex = paths[current_wapex].parent + _contender_wapex = paths.base_from_id[contender_wapex] + # print(f"{'RIGHT' if side else 'LEFT '} " + # f'current_wapex({F[_current_wapex]}) ' + # f'contender_wapex({F[_contender_wapex]})') + while (_current_wapex != _farside and + _contender_wapex >= 0 and + test(_new, _current_wapex, _contender_wapex)): + _funnel[not side] = _current_wapex + # wedge_end[not side] = current_wapex + current_wapex = contender_wapex + _current_wapex = _contender_wapex + contender_wapex = paths[current_wapex].parent + _contender_wapex = paths.base_from_id[contender_wapex] + # print(f"{'RIGHT' if side else 'LEFT '} " + # f'current_wapex({F[_current_wapex]}) ' + # f'contender_wapex({F[_contender_wapex]})') + _apex = _current_wapex + apex = current_wapex + else: + debug('inside') + yield from self._rate_wait_add(portal, _new, _apex, apex) + wedge_end[side] = paths.last_added + _funnel[side] = _new + else: + # infranear + debug('infranear') + current_wapex = wedge_end[side] + _current_wapex = paths.base_from_id[current_wapex] + # print(f'{F[_current_wapex]}') + contender_wapex = paths[current_wapex].parent + _contender_wapex = paths.base_from_id[contender_wapex] + while (_current_wapex != _nearside and + _contender_wapex >= 0 and + test(_current_wapex, _new, _contender_wapex)): + current_wapex = contender_wapex + _current_wapex = _contender_wapex + # print(f'{F[current_wapex]}') + contender_wapex = paths[current_wapex].parent + _contender_wapex = paths.base_from_id[contender_wapex] + yield from self._rate_wait_add( + portal, _new, _current_wapex, current_wapex) + wedge_end[side] = paths.last_added + + def _find_paths(self): + # print('[exp] starting _explore()') + G, P, M = self.G, self.P, self.M + d2roots = G.graph['d2roots'] + d2rootsRank = G.graph['d2rootsRank'] + prioqueue = [] + # `uncharted` records whether portals have been traversed + # (it is orientation-sensitive – two permutations) + uncharted = defaultdict(lambda: 2) + paths = self.paths = PathNodes() + self.uncharted = uncharted + self.bifurcation = None + I_path = defaultdict(dict) + self.I_path = I_path + + # set of portals (i.e. edges of P that are not used in G) + portal_set = set() + for i, (u, v) in enumerate(P.to_undirected(as_view=True).edges - + G.edges): + portal_set.add((u, v) if u < v else (v, u)) + self.portal_set = portal_set + + # launch channel traversers around the roots to the prioqueue + for r in range(-M, 0): + paths[r] = PseudoNode(r, r, None, 0., 0.) + paths.base_from_id[r] = r + paths.ids_from_base_sector[r, r] = [r] + for left in P.neighbors(r): + right = P[r][left]['cw'] + portal = (left, right) + portal_sorted = (right, left) if right < left else portal + if not (right in P[r] and portal_sorted in portal_set): + # (u, v, r) not a triangle or (u, v) is in G + continue + # flag initial portals as visited + self.uncharted[portal] = 0 + self.uncharted[right, left] = 0 + + sec_left = P[left][right]['ccw'] + while (left, sec_left) not in G.edges: + sec_left = P[left][sec_left]['ccw'] + if sec_left == r: + sec_left = NULL + + d_left, d_right = d2roots[left, r], d2roots[right, r] + # add the first pseudo-nodes to paths + wedge_end = [paths.add(left, sec_left, r, d_left, d_left), + paths.add(right, r, r, d_right, d_right)] + + # shortest paths for roots' P.neighbors is a straight line + I_path[left][sec_left], I_path[right][r] = wedge_end + + # prioritize by distance to the closest node of the portal + closest, d_closest = ( + (left, d_left) + if d2rootsRank[left, r] <= d2rootsRank[right, r] + else (right, d_right) + ) + heapq.heappush(prioqueue, ( + d_closest, portal, (closest, r), 0, + self._traverse_channel(r, r, [left, right], wedge_end, + self._advance_portal(left, right)) + )) + # TODO: this is arbitrary, should be documented somewhere (or removed) + MAX_ITER = 10000 + # process edges in the prioqueue + counter = 0 + # print(f'[exp] starting main loop, |prioqueue| = {len(prioqueue)}') + while len(prioqueue) > 0 and counter < MAX_ITER: + # safeguard against infinite loop + counter += 1 + # print(f'[exp] {counter}') + + if self.bifurcation is None: + # no bifurcation, pop the best traverser from the prioqueue + _d_contender, _portal, _hop, _, traverser = \ + heapq.heappop(prioqueue) + else: + # the last processed portal bifurcated + # add it to the queue and get the best traverser + + # make the traverser advance one portal + d_contender, portal, hop, is_better = next(self.bifurcation) + if is_better or uncharted[portal]: + # print(f'[exp]^pushing dist = {d_contender:.0f}, ' + # f'{self.n2s(*hop)} ') + _d_contender, _portal, _hop, _, traverser = ( + heapq.heappushpop(prioqueue, (d_contender, portal, + hop, counter, + self.bifurcation))) + # else: + # print(f'[exp]^traverser {self.n2s(*hop)} ' + # 'was dropped (no better than previous traverser).') + self.bifurcation = None + # print(f'[exp]_popped dist = {_d_contender:.0f}, ' + # f'{self.n2s(*_hop)} ') + try: + # make the traverser advance one portal + d_contender, portal, hop, is_better = next(traverser) + except StopIteration: + # print(f'[exp]_traverser {self.n2s(*hop)} was ' + # 'dropped (dead-end).') + pass + else: + if is_better or uncharted[portal]: + # print(f'[exp]_pushing dist = {d_contender:.0f}, ' + # f'{self.n2s(*hop)} ') + heapq.heappush(prioqueue, + (d_contender, portal, hop, counter, + traverser)) + # else: + # print(f'[exp]_traverser {self.n2s(*hop)} was ' + # 'dropped (no better that previous traverser).') + if counter == MAX_ITER: + warn('_find_paths() ended prematurely!') + info('_find_path looped {} times', counter) + + def _apply_all_best_paths(self, G: nx.Graph): + ''' + Update G with the paths found by `_find_paths()`. + ''' + get_best_path = self.get_best_path + for n in range(self.N): + for id in self.I_path[n].values(): + if id < 0: + # n is a root's neighbor + continue + path, dists = get_best_path(n) + nx.add_path(G, path, type='virtual') + + def plot_best_paths(self, + ax: Optional[matplotlib.axes.Axes] = None + ) -> matplotlib.axes.Axes: + ''' + Plot the subtrees of G (without gate edges) overlaid by the shortest + paths for all nodes. + ''' + K = nx.subgraph_view(self.G, + filter_edge=lambda u, v: u >= 0 and v >= 0) + ax = gplot(K, ax=ax) + J = nx.Graph() + J.add_nodes_from(self.G.nodes) + self._apply_all_best_paths(J) + landscape_angle = self.G.graph.get('landscape_angle') + if landscape_angle: + VertexC = rotate(self.VertexC, landscape_angle) + else: + VertexC = self.VertexC + nx.draw_networkx_edges(J, pos=VertexC, edge_color='y', + alpha=0.3, ax=ax) + return ax + + def plot_scaffolded(self, + ax: Optional[matplotlib.axes.Axes] = None + ) -> matplotlib.axes.Axes: + ''' + Plot the PlanarEmbedding of G, overlaid by the edges of G that coincide + with it. + ''' + return gplot(scaffolded(self.G, P=self.P), ax=ax, infobox=False) + + def create_detours(self, in_place: bool = False): + ''' + Replace all gate edges in G that cross other edges with detour paths. + If `in_place`, change the G given to PathFinder and return None, + else return new nx.Graph (G with detours). + ''' + if in_place: + G = self.G + else: + G = self.G.copy() + # this self.H is only for debugging purposes + self.H = G + if 'crossings' in G.graph: + # start by assumming that crossings will be fixed by detours + del G.graph['crossings'] + M, N = self.M, self.N + Xings = self.Xings + + if not Xings: + # no crossings were detected, nothing to do + return None if in_place else G.copy() + + gates2detour = set(gate for _, gate in Xings) + + clone2prime = [] + D = 0 + subtree_map = G.nodes(data='subtree') + # if G.graph['edges_created_by'] == 'make_MILP_length': + if G.graph['edges_created_by'][:5] == 'MILP.': + branching = G.graph['creation_options']['branching'] + else: + branching = True + # print(f'branching: {branching}') + paths = self.paths + I_path = self.I_path + for r, n in gates2detour: + subtree_id = G.nodes[n]['subtree'] + subtree_load = G.nodes[n]['load'] + # set of nodes to examine is different depending on `branching` + hookchoices = ( # all the subtree's nodes + [n for n, v in subtree_map if v == subtree_id] + if branching else + # only each subtree's head and tail + [n] + [n for n, v in subtree_map + if v == subtree_id and len(G._adj[n]) == 1]) + debug('hookchoices: {}', self.n2s(*hookchoices)) + + path_options = list(chain.from_iterable( + ((paths[id].dist, id, hook, sec) + for sec, id in I_path[hook].items()) + for hook in hookchoices)) + if not path_options: + error('subtree of node {} has no non-crossing paths to any ' + 'root: leaving gate as-is', F[n]) + # unable to fix this crossing + crossings = G.graph.get('crossings') + if crossings is None: + G.graph['crossings'] = [] + G.graph['crossings'].append((r, n)) + continue + dist, id, hook, sect = min(path_options) + debug('best: hook = {}, sector = {}, dist = {:.1f}', + F[hook], F[sect], dist) + + path = [hook] + dists = [] + pseudonode = paths[id] + while id >= 0: + dists.append(pseudonode.d_hop) + id = pseudonode.parent + path.append(paths.base_from_id[id]) + pseudonode = paths[id] + if not math.isclose(sum(dists), dist): + # assert math.isclose(sum(dists), dist), \ + error(f'distance sum ({sum(dists):.1f}) != ' + f'best distance ({dist:.1f}), hook = {F[hook]}, ' + f'path: {self.n2s(*path)}') + + debug('path: {}', self.n2s(*path)) + if len(path) < 2: + error('no path found for {}-{}', F[r], F[n]) + continue + Dinc = len(path) - 2 + Clone = list(range(N + D, N + D + Dinc)) + clone2prime.extend(path[1:-1]) + D += Dinc + G.add_nodes_from(((c, {'label': F[c], + 'type': 'detour', + 'subtree': subtree_id, + 'load': subtree_load}) + for c in Clone)) + if [n, r] != path: + G.remove_edge(r, n) + if r != path[-1]: + debug(f'root changed from {F[r]} to ' + f'{F[path[-1]]} for subtree of gate {F[n]}, now ' + f'hooked to {F[path[0]]}') + G.add_weighted_edges_from( + zip(path[:1] + Clone, Clone + path[-1:], dists), + weight='length') + for _, _, edgeD in G.edges(Clone, data=True): + edgeD.update(type='detour', reverse=True) + if Dinc > 0: + # an edge reaching root always has target < source + del G[Clone[-1]][path[-1]]['reverse'] + else: + debug(f'gate {F[n]}–{F[r]} touches a ' + 'node (touched node does not become a detour).') + if n != path[0]: + # the hook changed: update 'load' attributes of edges/nodes + debug('hook changed from {} to {}: recalculating loads', + F[n], F[path[0]]) + + for node in [n for n, v in subtree_map if v == subtree_id]: + if 'load' in G.nodes[node]: + del G.nodes[node]['load'] + + if Clone: + parent = Clone[0] + ref_load = subtree_load + G.nodes[parent]['load'] = 0 + else: + parent = path[-1] + ref_load = G.nodes[parent]['load'] + G.nodes[parent]['load'] = ref_load - subtree_load + total_parent_load = bfs_subtree_loads(G, parent, [path[0]], subtree_id) + assert total_parent_load == ref_load, \ + f'detour {F[n]}–{F[path[0]]}: load calculated ' \ + f'({total_parent_load}) != expected load ({ref_load})' + + fnT = np.arange(N + D + M) + fnT[N: N + D] = clone2prime + fnT[-M:] = range(-M, 0) + G.graph.update(D=D, fnT=fnT) + return None if in_place else G + + +# >>> BELLOW THIS POINT LAYS THE DEPRECATED CODE: midpoint based paths >>> +class PathSeeker(): + ''' + Deprecated earlier implementation of `PathFinder`. Uses the midpoints of + the portals to calculate distances for prioritization. Its reroute_gate() + method is broken, do not use. + + It still exists for the nice view of paths given by its `plot()` method. + (Besides, it has a more readable implementation of the funnel algorithm.) + ''' + + def __init__(self, G, branching=True, + only_if_crossings=True): + M = G.graph['M'] + VertexC = G.graph['VertexC'] + self.VertexC = VertexC + N = VertexC.shape[0] - M + + # this is just for facilitating printing debug messages + allnodes = np.arange(N + M) + allnodes[-M:] = range(-M, 0) + self.n2s = NodeStr(allnodes, N) + + info('BEGIN pathfinding on "{}" (#wtg = {})', G.graph['name'], N) + P = G.graph.get('planar') or planar_over_layout(G) + self.G, self.P, self.M, self.N = G, P, M, N + # sets of gates (one per root) that are not in the planar embedding + nonembed_Gates = tuple( + np.fromiter(set(G.neighbors(r)) - set(P.neighbors(r)), dtype=int) + for r in range(-M, 0)) + self.nonembed_Gates = nonembed_Gates + if G.graph['edges_created_by'][:5] == 'MILP.': + self.branching = G.graph['creation_options']['branching'] + else: + self.branching = branching + Xings = list(gateXing_iter(G, gates=nonembed_Gates)) + self.Xings = Xings + if not Xings and only_if_crossings: + # no crossings, there is no point in pathfinding + info('Graph has no crossings, skipping path-finding ' + '(pass `only_if_crossings=False` to force path-finding).') + return + self.create() + + def _get_scaffold(self) -> nx.Graph: + scaffold = getattr(self, 'scaffold', None) + if scaffold is None: + scaffold = scaffolded(self.G, P=self.P) + self.scaffold = scaffold + return scaffold + + def path_by_funnel(self, s, t, channel): + ''' + (only used by the legacy midpoint aproach, but can be used + independently as long as `self.VertexC` has the coordinates) + + Funnel algorithm (aka string pulling): + parameters: + `s`: source node + `t`: target node + `channel`: sequence of node pairs defining the portals + (must be ⟨left, right⟩ wrt direction s -> t) + returns: + `path`: sequence of nodes (including `s` and `t`) + ''' + # original algorithm description in section 4.4 of th MSc thesis: + # Efficient Triangulation-Based Pathfinding (2007, Douglas Jon Demyen) + # https://era.library.ualberta.ca/items/3075ea07-5eb5-44e9-b863-ae898bf00fe1 + + # also inspired by (C implementations): + # Ash Hamnett: Funnel Algorithm + # http://ahamnett.blogspot.com/2012/10/funnel-algorithm.html + # Simple Stupid Funnel Algorithm + # http://digestingduck.blogspot.com/2010/03/simple-stupid-funnel-algorithm.html?m=1 + # (but instead of resetting the portal when a new apex is chosen, this + # implementation keeps track of the nodes that need to be revisited) + + path = [s] + apex = s + # Left and Right deques are complicated to explain: they are needed + # for backtracking after one side remains stuck at a channel boundary + # violation while the other advances several portals before violating + # the funnel and triggering a new apex. + Left = deque() + Right = deque() + channel.append((t, t)) + left, right = channel[0] + cw, ccw = rotation_checkers_factory(self.VertexC) + + counter = 0 + for next_left, next_right in channel[1:]: + # log = (f'{counter:2d} ' + # f'⟨{F[left]}–{F[apex]}–{F[right]}⟩ ' + # + n2s(next_left, next_right)) + counter += 1 + # left side + if next_left != left: + # log += f' L:{n2s(next_left)} ' + if left == apex or cw(left, next_left, apex): + # log += ' bou:pass ' + # non-overlapping + # test overlap with other side + if cw(right, next_left, apex): + # overlap on the right + # log += ' opo:fail ' + # other side is the new apex + apex = right + path.append(apex) + while Right: + right = Right.popleft() + if (cw(right, next_left, apex) and + (not Right or + cw(right, Right[0], apex))): + apex = right + path.append(apex) + elif cw(right, next_right, apex): + Right.appendleft(right) + next_right = right + break + right = next_right + left = next_left + else: + # log += ' bou:fail ' + if not Left or next_left != Left[-1]: + Left.append(next_left) + # right side + if next_right != right: + # log += f' R:{n2s(next_right)} ' + # test overlap with boundary + if right == apex or cw(next_right, right, apex): + # log += ' bou:pass ' + # non-overlapping + # test overlap with other side + if cw(next_right, left, apex): + # overlap on the left + # log += ' opo:fail ' + # other side is the new apex + apex = left + path.append(apex) + while Left: + left = Left.popleft() + if (cw(next_right, left, apex) and + (not Left or + cw(Left[0], left, apex))): + apex = left + path.append(apex) + elif cw(next_left, left, apex): + Left.appendleft(left) + next_left = left + break + left = next_left + right = next_right + else: + # log += ' bou:fail ' + if not Right or next_right != Right[-1]: + Right.append(next_right) + # print(log, f'⟨{F[left]}–{F[apex]}–{F[right]}⟩', + # n2s(*path) if path else '') + if path[-1] != t: + path.append(t) + return path + + def plot(self, ax): + if not getattr(self, 'Gmidpt', False): + self.create() + ax = gplot(self._get_scaffold(), node_tag='label', ax=ax) + nx.draw_networkx_edges(self.Gmidpt, pos=self.PortalC, ax=ax, edge_color='y', + arrowsize=3, node_size=0, alpha=0.3) + return ax + + def create(self): + G = self.G + P = self.P + M = self.M + VertexC = self.VertexC + idx_from_portal = {} + # N_portals = (P.number_of_edges()//2 + # - G.number_of_edges() + # + sum((len(Gate) for Gate in self.nonembed_Gates))) + all_portals = P.to_undirected(as_view=True).edges - G.edges + N_portals = len(all_portals) + portal_from_idx = np.empty((N_portals, 2), dtype=int) + for i, (u, v) in enumerate(all_portals): + u, v = (u, v) if u < v else (v, u) + portal_from_idx[i] = u, v + idx_from_portal[u, v] = i + self.idx_from_portal = idx_from_portal + self.portal_from_idx = portal_from_idx + + self.Gmidpt = nx.DiGraph() + self.Gmidpt.add_nodes_from(range(-M, 0), dist=0.) + PortalC = np.empty((N_portals + M, 2), dtype=float) + PortalC[:N_portals] = (VertexC[portal_from_idx[:, 0]] + + VertexC[portal_from_idx[:, 1]]) / 2 + PortalC[-M:] = VertexC[-M:] + self.PortalC = PortalC + + # items in que prioqueue: + # (distance-to-root from portal following midpoints, + # parent idx, portal idx, dist parent-portal, + # portal vertex low, portal vertex high, + # rotation from low->high for next vertex, + # rootsector vertex idx) + prioqueue = [] + + # add edges around the roots to the prioqueue + for r in range(-M, 0): + for u in P.neighbors(r): + v = P[u][r]['cw'] + (u, v), cw = ((u, v), True) if u < v else ((v, u), False) + if not (v in P[r] and (u, v) in idx_from_portal): + # (u, v, r) not a triangle or (u, v) is in G + continue + p = idx_from_portal[(u, v)] + d_hop = np.hypot(*(PortalC[p] - VertexC[r]).T) + # for the 1st hop: d_path = d_hop + heapq.heappush(prioqueue, (d_hop, r, p, d_hop, u, v, cw, p)) + + # TODO: this is arbitrary, should be documented somewhere (or removed) + MAX_ITER = 10000 + # process edges in the prioqueue + counter = 0 + while len(prioqueue) > 0 and counter < MAX_ITER: + # safeguard against infinite loop + counter += 1 + + # get next item in the prioqueue + (dist, parent, p, d_hop, + u, v, cw, rootsector) = heapq.heappop(prioqueue) + if p in self.Gmidpt: + # some other route reached p at a lower cost + continue + # rev is true if the portal nodes are ordered (right, left) + # instead of (left, right) + self.Gmidpt.add_node(p, dist=dist, rootsector=rootsector, rev=not cw) + self.Gmidpt.add_edge(p, parent, length=d_hop) + + # look for children portals + spin = 'cw' if cw else 'ccw' + n = P[u][v][spin] + if n not in P[v] or P[u][n][spin] == v or n < 0: + # (u, v, n) not a new triangle + continue + # examine the other two sides of the triangle + for (s, t) in ((u, n), (n, v)): + (s, t), next_cw = ((s, t), cw) if s < t else ((t, s), not cw) + if (s, t) not in idx_from_portal: + # (s, t, r) not a triangle or (s, t) is in G + continue + next_p = idx_from_portal[(s, t)] + next_dist = np.hypot(*(PortalC[next_p] - PortalC[p]).T) + heapq.heappush(prioqueue, + (dist + next_dist, p, next_p, next_dist, + s, t, next_cw, rootsector)) + if counter >= MAX_ITER: + print('pathfinder generation INTERRUPTED!!!' + ' reached iterations limit = ', counter) + + def check(self): + # TODO: check() and _check_recursive() belong to testing code + for r in range(-self.G.graph['M'], 0): + for pred in self.Gmidpt.predecessors(r): + self.check_recursive( + pred, self.Gmidpt[pred][r]['length'], pred) + + def check_recursive(self, entry, prev_dist, prev_rootsector): + # TODO: check() and _check_recursive() belong to testing code + rootsector = self.Gmidpt.nodes[entry]['rootsector'] + if rootsector != prev_rootsector: + print('rootsector changed:', + self.n2s(*self.portal_from_idx[entry]), + 'previously: ', + self.n2s(*self.portal_from_idx[prev_rootsector]), + 'now: ', self.n2s(*self.portal_from_idx[rootsector])) + for pred in self.Gmidpt.predecessors(entry): + pred_dist = self.Gmidpt.nodes[pred]['dist'] + cummulative = prev_dist + self.Gmidpt[pred][entry]['length'] + diff = cummulative - pred_dist + if diff != 0: + print('distance mismatch:', + self.n2s(*self.portal_from_idx[entry]), + self.n2s(*self.portal_from_idx[pred]), + f'diff = {diff:.0f}') + self._check_recursive(pred, cummulative, rootsector) + + def reroute_gate(self, s): + ''' + DEPRECATED: this algorithm is defective and will not be fixed + + Return shortest path (sequence of nodes, possibly with detours) to link + the subtree with gate `s` to a root node (another node from the subtree + may be chosen as gate). + :param s: some gate node + Returns: + tuple(actual_dist, pred_dist, path, dists) + ''' + # TODO: It is possible to have edges in self.P which were not + # included in the pathfinder graph. These are confined between + # the convex hull and a single subtree. A corner case would be + # an entire subtree in that region, for which no path can be + # found (it would require leaving the convex hull). + + G = self.G + P = self.P + subtree = G.nodes[s]['subtree'] + # print(F[s], subtree) + # incumbent = defaultdict(dict) # by rootsector, then by dist + subtree_pdict = defaultdict(list) + + # log = '' + # STEP 1) Get the portals around the nodes of the subtree. + nodes2check = (n for n, d in G.nodes(data=True) + if n >= 0 and d['subtree'] == subtree) + if not self.branching: + nodes2check = (n for n in nodes2check if n == s or len(G[n]) == 1) + for n in nodes2check: + # log += F[n] + for nbr in P.neighbors(n): + # print(log, F[nbr]) + if nbr < 0: + # subtree includes a neighbor of the root + print('WARNING: `shortest_gate()` should not be called for' + ' nodes whose subtree connects to root through an ' + "extended Delaunay edge. Something is wrong.") + continue + v = P[nbr][n]['cw'] + if v != P[n][nbr]['ccw']: + continue + u, v = (nbr, v) if nbr < v else (v, nbr) + # print(n2s(u, v)) + p = self.idx_from_portal.get((u, v)) + if p is not None and p in self.Gmidpt: + # log += n2s(*self.portal_from_idx[p]) + rootsector = self.Gmidpt.nodes[p]['rootsector'] + subtree_pdict[rootsector].append((p, n)) + # log = '' + + # print(log) + # print(subtree_pdict) + + # STEP 2) For each rootsector, choose the dominating portal and then + # the dominating node. Output is a unique portal sequence per + # rootsector. + min4rootsector = {} + for rootsector, pn_pairs in subtree_pdict.items(): + # start with the first item as incumbent + p, n = pn_pairs[0] + portals = [p] + incumbent = [n] + p, = self.Gmidpt.successors(p) + while p > 0: + portals.append(p) + p, = self.Gmidpt.successors(p) + r = p + for p, n in pn_pairs[1:]: + try: + i = portals.index(p) + if i == 0: + # p is tied with the incumbent + # TODO: probably this does not happen + incumbent.append(n) + print('WARNING: incumbent.append(n) actually happens!', + *(F[n] for n in incumbent)) + else: + # p dominates the previous incumbent + incumbent = [n] + portals = portals[i:] + except ValueError: + # p is further away than the incumbent + pass + # now portals[0] is the dominating portal + # calculate the distances from the portal + # to each contending node + dists = np.hypot(*(self.PortalC[portals[0]] - + self.VertexC[incumbent]).T) + # TODO: if the `incumbent.append(n)` indeed does not + # happen, incumbent can be a scalar value + i_min = np.argmin(dists) + print(f'{i_min},', *[f'{d:.0f}' for d in dists]) + # store: (node, root, sequence of portal indices, + # reference distance (through portal midpoints)) + min4rootsector[rootsector] = (incumbent[i_min], r, portals, + self.Gmidpt.nodes[p]['dist'] + dists[i_min]) + + # log = '' + # for k, (n, r, portals, dist) in min4rootsector.items(): + # log += (n2s(*self.portal_from_idx[k]) + # + f' {F[n]}, {F[r]}, {len(portals)}, {dist:.0f}\n' + # print(log) + + # STEP 3) Calculate the distances of the shortest paths to root and + # pick the path with the shortest distance. + # final_paths = {} + contenders = [] + for rootsector, (n, r, portals, pred_dist) in min4rootsector.items(): + portal_pairs = ((self.portal_from_idx[p], + self.Gmidpt.nodes[p]['rev']) + for p in portals) + channel = [(u, v) if not rev else (v, u) + for (u, v), rev in portal_pairs] + print('channel:', *(f'{F[s]}–{F[t]}' for s, t in channel)) + path = self.path_by_funnel(n, r, channel) + print('path:', '–'.join(f'{F[n]}' for n in path)) + segments = (self.VertexC[path[:-1]] - + self.VertexC[path[1:]]) + dists = np.hypot(*segments.T) + actual_dist = dists.sum() + contenders.append((actual_dist, pred_dist, path, + dists, channel[0])) + # ranking.append((total_dist, pred_dist, rootsector)) + # final_paths[rootsector] = (path, dists, total_dist) + + chosen = min(contenders) + + # STEP 4) Check if the last link crosses any branches of the path tree. + # (that can only happen in the first triangle) + _, _, path, _, (u, v) = chosen + if len(path) > 2: + head = path[0] + tri_side = (u, head) if u < head else (head, u) + p1 = self.idx_from_portal.get(tri_side) + if p1 is not None: + tri_side = (v, head) if v < head else (head, v) + p2 = self.idx_from_portal.get(tri_side) + if p2 is not None and (p1 in self.Gmidpt._succ[p2] or + p2 in self.Gmidpt._succ[p1]): + print('WARNING: Path crossed in triangle ' + f'{self.n2s(head, u, v)}') + + return chosen[:-1] diff --git a/ed_win/plotting_scripts/plotting.py b/ed_win/plotting_scripts/plotting.py new file mode 100644 index 0000000..18c59f3 --- /dev/null +++ b/ed_win/plotting_scripts/plotting.py @@ -0,0 +1,575 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +from collections import defaultdict +from collections.abc import Sequence +from typing import Optional + +import matplotlib.pyplot as plt +import networkx as nx +import numpy as np +from matplotlib import animation +from matplotlib.patches import Polygon +from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar + +from .geometric import make_graph_metrics, rotate +from .interarraylib import calcload + + +FONTSIZE_LABEL = 6 +FONTSIZE_LOAD = 8 +FONTSIZE_ROOT_LABEL = 6 +FONTSIZE_LEGEND_BOX = 7 +FONTSIZE_LEGEND_STRIP = 6 +NODESIZE = 18 +NODESIZE_LABELED = 70 +NODESIZE_LABELED_ROOT = 28 +NODESIZE_DETOUR = 80 +NODESIZE_LABELED_DETOUR = 150 + + +class LayoutPlotter(): + '''Plotter for making step-by-step animations. The heuristics actually + generate a log of actions and the plotter is only called after the entire + process is over. This class is only instantiated inside of `animate()`.''' + + edge_color = 'crimson' + root_color = 'lawngreen' + node_tag = 'label' + colors = plt.cm.get_cmap('tab20', 20).colors + + def __init__(self, G_base, ax=None, dpi=None, node_tag='label'): + # fail early if graph does not have a log + loglist = G_base.graph['log'] + if 'has_loads' not in G_base.graph: + calcload(G_base) + + if dpi is None: + dpi = plt.rcParams['figure.dpi'] + # self.mm = mm = 25.4*dpi + self.node_tag = node_tag + self.G_base = G_base + self.root_node_size = (NODESIZE_LABELED_ROOT + if self.node_tag is not None else + NODESIZE) + self.node_size = NODESIZE_LABELED if self.node_tag is not None else NODESIZE + + log = [(0, [['nop', None]])] + iprev = 0 + for i, *entry in loglist: + # print(i) + if i != iprev: + out = [entry] + iprev = i + log.append((i, out)) + else: + out.append(entry) + + log.append((i, [['end', None]])) + self.log = log + + VertexC = G_base.graph['VertexC'] + self.VertexC = VertexC + M = G_base.graph['M'] + self.M = M + N = G_base.number_of_nodes() - M + self.N = N + self.fnT = G_base.graph.get('fnT') + pos = dict(zip(range(N), VertexC[:N])) + pos |= dict(zip(range(-M, 0), VertexC[-M:])) + D = G_base.graph.get('D') + if D is not None: + N -= D + self.pos = pos + + G = nx.Graph(name=G_base.name, M=M, VertexC=VertexC) + G.add_nodes_from(G_base.nodes(data=True)) + # make star graph + make_graph_metrics(G) + d2roots = G.graph['d2roots'] + for n in range(N): + # root = G_base.nodes[n]['root'] + root = G.nodes[n]['root'] + G.add_edge(root, n, length=d2roots[n, root]) + # for node in G.nbunch_iter(range(N, N + D)): + # G[node]['color'] = 'none' + self.G = G + + subtrees = G.nodes(data='subtree', default=-1) + self.node_colors = np.array([self.colors[subtrees[n] % + len(self.colors)] + for n in range(N)]) + Subtree = defaultdict(list) + for node, subtreeI in G.nodes(data='subtree'): + if subtreeI is None or node >= N: + continue + Subtree[subtreeI].append(node) + self.Subtree = Subtree + self.uncolored = set(Subtree.keys()) + self.DetourNodeA = {} + + if ax is None: + # limX, limY = 1920/dpi, 1080/dpi + limX, limY = 1440 / dpi, 900 / dpi + lR = limX / limY + boundary = G_base.graph['boundary'] + XYrange = np.abs(np.amax(boundary, axis=0) - np.amin(boundary, axis=0)) + ratio = XYrange[0] / XYrange[1] + figsize = (limX, limX / ratio) if ratio > lR else (limY * ratio, limY) + self.fig = plt.figure(figsize=figsize) + # self.fig.facecolor = '#1b1c17' + self.init_plt() + + def init_plt(self): + G = self.G + M = self.M + pos = self.pos + # ax = self.ax + ax = self.fig.add_subplot(aspect='equal') + ax.axis('off') + # ax.facecolor = '#1b1c17' + self.ax = ax + + redraw = [] + # draw farm boundary + # area_polygon = Polygon(self.G_base.graph['boundary'], + # color='#111111', zorder=0) + area_polygon = Polygon(self.G_base.graph['boundary'], color='black', zorder=0) + self.boundaryA = ax.add_patch(area_polygon) + redraw.append(self.boundaryA) + ax.update_datalim(area_polygon.get_xy()) + ax.autoscale() + ax.set_aspect('equal') + + # draw root nodes + roots = range(-M, 0) + RootL = {r: G.nodes[r]['label'] for r in roots[::-1]} + redraw.append(nx.draw_networkx_nodes( + G, pos, ax=ax, nodelist=roots, node_color=self.root_color, + node_shape='s', node_size=self.root_node_size)) + + # draw regular nodes, one subtree at a time + Subtree = self.Subtree + SubtreeA = np.empty((len(Subtree)), dtype=object) + for subtreeI, nodes in Subtree.items(): + SubtreeA[subtreeI] = nx.draw_networkx_nodes( + G, pos, ax=ax, nodelist=Subtree[subtreeI], node_color=[self.colors[-1]], + node_size=self.node_size) + redraw.append(SubtreeA[subtreeI]) + self.SubtreeA = SubtreeA + + # draw labels + font_size = {'load': FONTSIZE_LOAD, 'label': FONTSIZE_LABEL} + node_tag = self.node_tag + if node_tag is not None: + if node_tag == 'load' and 'has_loads' not in G.graph: + node_tag = 'label' + labels = nx.get_node_attributes(G, node_tag) + for root in roots: + if root in labels: + labels.pop(root) + self.labelsA = nx.draw_networkx_labels( + G, pos, ax=ax, font_size=font_size[node_tag], + labels=labels) + redraw.extend(self.labelsA.values()) + # root nodes' labels + redraw.extend(nx.draw_networkx_labels(G, pos, ax=ax, labels=RootL, + font_size=FONTSIZE_ROOT_LABEL + ).values()) + redraw.append(self.draw_edges()) + + # create text element for iteration number + self.iteration = ax.text(0.01, 0.99, 'i = 0', ha='left', va='top', + transform=ax.transAxes, + fontname='DejaVu Sans Mono', + color='white') + # transform=ax.transAxes, fontname='Inconsolata') + # transform=ax.transAxes, fontname='Iosevka') + redraw.append(self.iteration) + # create text element for total length + self.length = ax.text(0.99, 0.99, f'{G.size(weight="length"):.0f} m', + ha='right', va='top', transform=ax.transAxes, + fontname='DejaVu Sans Mono', + color='white') + # fontname='Inconsolata') + # fontname='Iosevka') + redraw.append(self.length) + return redraw + + def draw_edges(self): + G = self.G + edge_colors = [color for u, v, color in + G.edges(data='color', default=self.edge_color)] + edge_style = [style for s, t, style in + G.edges(data='style', default='solid')] + edgesA = nx.draw_networkx_edges( + G, self.pos, ax=self.ax, edge_color=edge_colors, + style=edge_style) + self.prevEdgesA = edgesA + return edgesA + + def update(self, step): + redraw = [] + # n2s = NodeStr(self.fnT, self.N) + detourprop = dict(style='dashed', color='yellow') + G = self.G + pos = self.pos + VertexC = self.VertexC + # TODO: clear highlighting from previous iteration + + i, operations = step + self.iteration.set_text('i = ' + f'{i:d}'.rjust(3, ' ')) + redraw.append(self.iteration) + for oper, args in operations: + if oper == 'addE': + # if args in self.G_base.edges: + # length = self.G_base[args]['length'] + # else: + # u, v = args + # length = np.hypot(*(VertexC[u] - VertexC[v]).T) + G.add_edge(*args, length=self.G_base.edges[args]['length']) + elif oper == 'remE': + G.remove_edge(*args) + elif oper == 'addDE': + s, t, s_, t_ = args + length = np.hypot(*(VertexC[s_] - VertexC[t_]).T) + G.add_edge(s, t, **detourprop, length=length) + elif oper == 'addDN': + t_, new = args + G.add_node(new) + pos[new] = VertexC[t_] + self.DetourNodeA[new] = nx.draw_networkx_nodes( + G, pos, ax=self.ax, nodelist=[new], alpha=0.4, + edgecolors='orange', node_color='none', + node_size=NODESIZE_LABELED_DETOUR) + redraw.append(self.DetourNodeA[new]) + elif oper == 'movDN': + hook, corner, hook_, corner_ = args + root = self.G_base.nodes[hook]['root'] + pos[corner] = VertexC[corner_] + if corner > 0: + self.DetourNodeA[corner].remove() + redraw.append(self.DetourNodeA[corner]) + self.DetourNodeA[corner] = nx.draw_networkx_nodes( + G, pos, ax=self.ax, nodelist=[corner], alpha=0.4, + edgecolors='orange', node_color='none', + node_size=NODESIZE_LABELED_DETOUR) + redraw.append(self.DetourNodeA[corner]) + hook2cornerL = np.hypot(*(VertexC[hook_] - + VertexC[corner_]).T) + # corner2rootL = self.G_base.graph['d2roots'][t_, root] + G.edges[hook, corner]['length'] = hook2cornerL + # G.edges[blocked, root]['length'] = corner2rootL + elif oper == 'finalG': + # TODO: this coloring of the subtree is misleading for the + # cases where the gate is marked as final before the full + # capacity is reached, causing nodes still not part of the + # final subtree to receive its color too early + gate, root = args + subtreeI = G.nodes[gate]['subtree'] + self.SubtreeA[subtreeI].remove() + nodes = self.Subtree[subtreeI] + self.SubtreeA[subtreeI] = nx.draw_networkx_nodes( + G, pos, ax=self.ax, node_size=self.node_size, + nodelist=nodes, node_color=self.node_colors[nodes]) + redraw.append(self.SubtreeA[subtreeI]) + # print(i, self.uncolored, f' - {{{subtreeI}}}') + # self.uncolored.remove(subtreeI) + self.uncolored.discard(subtreeI) + elif oper == 'remN': + G.remove_node(args) + self.DetourNodeA[args].remove() + redraw.append(self.DetourNodeA[args]) + del self.DetourNodeA[args] + elif oper == 'end': + # color the remaining subtrees + for subtreeI in self.uncolored: + self.SubtreeA[subtreeI].remove() + nodes = self.Subtree[subtreeI] + self.SubtreeA[subtreeI] = nx.draw_networkx_nodes( + G, pos, ax=self.ax, node_size=self.node_size, + nodelist=nodes, node_color=self.node_colors[nodes]) + redraw.append(self.SubtreeA[subtreeI]) + # make text bold to mark the last frame + self.length.set_color('yellow') + self.iteration.set_color('yellow') + elif oper == 'nop': + pass + else: + print('Unknown operation:', oper) + + self.length.set_text(f'{G.size(weight="length"):.0f} m') + redraw.append(self.length) + self.prevEdgesA.remove() + self.prevEdgesA.set_visible(False) + redraw.append(self.prevEdgesA) + redraw.append(self.draw_edges()) + return redraw + + +def animate(G, interval=250, blit=True, workpath='./tmp/', node_tag='label', + savepath='./videos/', remove_apng=True, use_apng2gif=False): + # old_dpi = plt.rcParams['figure.dpi'] + # dpi = plt.rcParams['figure.dpi'] = 192 + # layplt = LayoutPlotter(G, dpi=dpi) + layplt = LayoutPlotter(G, dpi=192, node_tag=node_tag) + savefig_kwargs = {'facecolor': '#1b1c17'} + anim = animation.FuncAnimation( + layplt.fig, layplt.update, frames=layplt.log, interval=interval, + blit=blit) + if use_apng2gif: + print('apng2gif is disabled in the source code.') + # fname = f'{G.name}_{G.graph["edges_created_by"]}_' \ + # f'{G.graph["capacity"]}.apng' + # from numpngw import AnimatedPNGWriter + # writer = AnimatedPNGWriter(fps=1000/interval) + # anim.save(workpath + fname, writer=writer, + # savefig_kwargs=savefig_kwargs) + # subprocess.run(['apng2gif', workpath + fname, savepath + fname[:-4] + 'gif']) + # if remove_apng: + # os.remove(workpath + fname) + else: + fname = f'{G.name}_{G.graph["edges_created_by"]}_' \ + f'{G.graph["capacity"]}.gif' + writer = animation.PillowWriter(fps=1000 / interval) + anim.save(savepath + fname, writer=writer, + savefig_kwargs=savefig_kwargs) + from pygifsicle import gifsicle + gifsicle(sources=savepath + fname, options=['--optimize=3']) + # plt.rcParams['figure.dpi'] = old_dpi + return fname + + +def gplot(G, ax=None, node_tag='label', edge_exemption=False, figlims=(5, 6), + landscape=True, infobox=True): + '''NetworkX graph plotting function. + `node_tag` in [None, 'load', 'label'] + (or other key in nodes's dict)''' + figsize = plt.rcParams['figure.figsize'] + dark = plt.rcParams['figure.facecolor'] != 'white' + + root_size = NODESIZE_LABELED_ROOT if node_tag is not None else NODESIZE + detour_size = NODESIZE_LABELED_DETOUR if node_tag is not None else NODESIZE_DETOUR + node_size = NODESIZE_LABELED if node_tag is not None else NODESIZE + + type2color = {} + type2style = dict( + detour='dashed', + scaffold='dotted', + extended='dashed', + delaunay='solid', + unspecified='solid', + ) + if dark: + scalebar = False + type2color.update( + detour='darkorange', + scaffold='gray', + delaunay='darkcyan', + extended='darkcyan', + unspecified='crimson', + ) + root_color = 'lawngreen' + node_edge = 'none' + detour_ring = 'orange' + polygon_edge = 'none' + polygon_face = '#111111' + else: + scalebar = True + type2color.update( + detour='royalblue', + scaffold='gray', + delaunay='black', + extended='black', + unspecified='firebrick', + ) + root_color = 'black' if node_tag is None else 'yellow' + node_edge = 'black' + detour_ring = 'deepskyblue' + polygon_edge = '#444444' + polygon_face = 'whitesmoke' + + landscape_angle = G.graph.get('landscape_angle') + if landscape and landscape_angle: + # landscape_angle is not None and not 0 + VertexC = rotate(G.graph['VertexC'], landscape_angle) + else: + VertexC = G.graph['VertexC'] + M = G.graph['M'] + N = G.number_of_nodes() - M + D = G.graph.get('D') + + # draw farm boundary + if 'boundary' in G.graph: + BoundaryC = (rotate(G.graph['boundary'], landscape_angle) + if landscape and landscape_angle else + G.graph['boundary']) + if ax is None and not dark: + limX, limY = figlims + r = limX / limY + XYrange = np.abs(np.amax(BoundaryC, axis=0) - + np.amin(BoundaryC, axis=0)) + d = XYrange[0] / XYrange[1] + if d < r: + figsize = (limY * d, limY) + else: + figsize = (limX, limX / d) + + area_polygon = Polygon(BoundaryC, zorder=0, linestyle='--', + facecolor=polygon_face, edgecolor=polygon_edge, + linewidth=0.3) + if ax is None: + fig, ax = plt.subplots(figsize=figsize) + ax.add_patch(area_polygon) + ax.update_datalim(area_polygon.get_xy()) + ax.autoscale() + elif ax is None: + fig, ax = plt.subplots(figsize=figsize) + ax.axis('off') + + ax.set_aspect('equal') + # setup + roots = range(-M, 0) + pos = dict(zip(range(N), VertexC[:N])) | dict(zip(roots, VertexC[-M:])) + if D is not None: + N -= D + fnT = G.graph.get('fnT') + detour = range(N, N + D) + pos |= dict(zip(detour, VertexC[fnT[detour]])) + RootL = {r: G.nodes[r]['label'] for r in roots[::-1]} + + colors = plt.cm.get_cmap('tab20', 20).colors + # default value for subtree (i.e. color for unconnected nodes) + # is the last color of the tab20 colormap (i.e. 19) + subtrees = G.nodes(data='subtree', default=19) + node_colors = [colors[subtrees[n] % len(colors)] for n in range(N)] + + # draw edges + nx.draw_networkx_edges(G, pos, ax=ax, edge_color=type2color['unspecified'], + style=type2style['unspecified'], label='direct', + edgelist=[(u, v) for u, v, t in G.edges.data('type') + if t is None]) + for edge_type in type2style: + nx.draw_networkx_edges(G, pos, ax=ax, edge_color=type2color[edge_type], + style=type2style[edge_type], label=edge_type, + edgelist=[(u, v) for u, v, t + in G.edges.data('type') + if t == edge_type]) + + # draw nodes + if D is not None: + # draw circunferences around nodes that have Detour clones + nx.draw_networkx_nodes(G, pos, ax=ax, nodelist=detour, alpha=0.4, + edgecolors=detour_ring, node_color='none', + node_size=detour_size, + label='corner') + nx.draw_networkx_nodes(G, pos, ax=ax, nodelist=roots, linewidths=0.2, + node_color=root_color, edgecolors=node_edge, + node_size=root_size, node_shape='s', + label='OSS') + nx.draw_networkx_nodes(G, pos, nodelist=range(N), edgecolors=node_edge, + ax=ax, node_color=node_colors, node_size=node_size, + linewidths=0.2, label='WTG') + + # draw labels + font_size = dict(load=FONTSIZE_LOAD, + label=FONTSIZE_LABEL, + tag=FONTSIZE_ROOT_LABEL) + if node_tag is not None: + if node_tag == 'load' and 'has_loads' not in G.graph: + node_tag = 'label' + labels = nx.get_node_attributes(G, node_tag) + for root in roots: + if root in labels: + labels.pop(root) + if D is not None: + for det in detour: + if det in labels: + labels.pop(det) + nx.draw_networkx_labels(G, pos, ax=ax, font_size=font_size[node_tag], + labels=labels) + # root nodes' labels + if node_tag is not None: + nx.draw_networkx_labels(G, pos, ax=ax, font_size=FONTSIZE_ROOT_LABEL, + labels=RootL) + + if scalebar: + bar = AnchoredSizeBar(ax.transData, 1000, '1 km', 'lower right', + frameon=False) + ax.add_artist(bar) + + if infobox: + if 'capacity' in G.graph: + info = [f'$\\kappa$ = {G.graph["capacity"]}, ' + f'$N$ = {N}'] + feeder_info = [f'$\\phi_{{{rootL}}}$ = {len(G[r])}' + for r, rootL in RootL.items()] + if 'overfed' in G.graph: + feeder_info = [fi + f' ({100*(overfed - 1):+.0f}%)' + for fi, overfed in + zip(feeder_info, G.graph['overfed'][::-1])] + info.extend(feeder_info) + # legend.append(', '.join(feeder_info)) + Gʹ = nx.subgraph_view(G, + filter_edge=lambda u, v: 'length' in G[u][v]) + length = Gʹ.size(weight="length") + intdigits = int(np.floor(np.log10(length))) + 1 + info.append(f'Σl = {round(length, max(0, 5 - intdigits))} m') + # assert Gʹ.number_of_edges() == G.number_of_nodes() - 1, \ + # f'{Gʹ.number_of_edges()} != {G.number_of_nodes()}' + # for field, sym in (('weight', 'w'), ('length', 'l')): + # for field, sym in (('length', ''),): + # weight = field if all([(field in data) + # for _, _, data in G.edges.data()]) else None + # legend.append('Σ{} = {:.0f}'.format(sym, G.size(weight=weight)) + + # ' m' if field == 'length' else '') + if ('has_costs' in G.graph): + info.append('{:.0f} €'.format(G.size(weight='cost'))) + if 'capacity' in G.graph: + infobox = ax.legend([], fontsize=FONTSIZE_LEGEND_BOX, + title='\n'.join(info), labelspacing=0) + # loc='upper right' + # bbox_to_anchor=(-0.04, 0.80, 1.08, 0) + # bbox_to_anchor=(-0.04, 1.03, 1.08, 0) + plt.setp(infobox.get_title(), multialignment='center') + # ax.legend(title='\n'.join(legend)) + # legend1 = pyplot.legend(plot_lines[0], ["algo1", "algo2", "algo3"], loc=1) + # pyplot.legend([l[0] for l in plot_lines], parameters, loc=4) + # ax.add_artist(legstrip) + if not dark: + ax.legend(ncol=8, fontsize=FONTSIZE_LEGEND_STRIP, loc='lower center', + frameon=False, bbox_to_anchor=(0.5, -0.07), + columnspacing=1, handletextpad=0.3) + if 'capacity' in G.graph and infobox: + ax.add_artist(infobox) + return ax + + +def compare(positional=None, **title2G_dict): + ''' + Plot layouts side by side. dict keys are inserted in the title. + Arguments must be either a sequence of graphs or multiple + `keyword`=«graph_instance»`. + ''' + if positional is not None: + if isinstance(positional, Sequence): + title2G_dict |= {chr(i): val for i, val in + enumerate(positional, start=ord('A'))} + else: + title2G_dict[''] = positional + fig, axes = plt.subplots(1, len(title2G_dict), squeeze=False) + for ax, (title, G) in zip(axes.ravel(), title2G_dict.items()): + gplot(G, ax=ax, node_tag=None) + creator = G.graph.get("edges_created_by", 'no edges') + ax.set_title(f'{title} – {G.graph["name"]} ' + f'({creator})') + + +def scaffolded(G: nx.Graph, P: Optional[nx.PlanarEmbedding] = None) -> nx.Graph: + scaff = nx.Graph(G.graph['planar'] if P is None else P) + scaff.graph.update(G.graph) + for n, d in scaff.nodes(data=True): + d.update(G.nodes[n]) + for i, (u, v) in enumerate(scaff.edges - G.edges): + scaff[u][v]['type'] = 'scaffold' + return scaff diff --git a/ed_win/plotting_scripts/priorityqueue.py b/ed_win/plotting_scripts/priorityqueue.py new file mode 100644 index 0000000..9da3e03 --- /dev/null +++ b/ed_win/plotting_scripts/priorityqueue.py @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +from heapq import heappush, heappop +import itertools + + +class PriorityQueue(list): + + def __init__(self): + super().__init__(self) + # self.entries = [] + self.tags = {} + self.counter = itertools.count() + + def add(self, priority, tag, payload): + '''lowest priority pops first, payload cannot be None. + an addition with an already existing tag will cancel the + previous entry.''' + if payload is None: + raise ValueError('payload cannot be None.') + if tag in self.tags: + self.cancel(tag) + entry = [priority, next(self.counter), tag, payload] + self.tags[tag] = entry + heappush(self, entry) + + def strip(self): + '''removes all cancelled entries from the queue top''' + while self: + if self[0][-1] is None: + heappop(self) + else: + break + + def cancel(self, tag): + entry = self.tags.pop(tag) + entry[-1] = None + self.strip() + + def top(self): + 'returns the payload with lowest priority' + priority, count, tag, payload = heappop(self) + del self.tags[tag] + self.strip() + return tag, payload diff --git a/ed_win/plotting_scripts/storage.py b/ed_win/plotting_scripts/storage.py new file mode 100644 index 0000000..a15a434 --- /dev/null +++ b/ed_win/plotting_scripts/storage.py @@ -0,0 +1,254 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import json +import pickle +from collections.abc import Sequence +from hashlib import sha256 +from socket import getfqdn, gethostname + +import networkx as nx +import numpy as np +from pony.orm import db_session + +from .interarraylib import calcload +from .utils import NodeTagger + +# Coordinates use arrays of floats. +# Somehow, nodesets with the same coordinates were getting different digests, +# when the code ran on different computers. +# Rouding to a fixed (small) number of decimal place to fix it. +COORDINATES_DECIMAL_PLACES = 2 + +F = NodeTagger() + + +def graph_from_edgeset(edgeset): + nodeset = edgeset.nodes + VertexC = pickle.loads(nodeset.VertexC) + N = nodeset.N + M = nodeset.M + pickled_misc = edgeset.misc + if pickled_misc is None: + creator = edgeset.method.funname + iterations = 1 + else: + misc = pickle.loads(pickled_misc) + creator = misc['edges_created_by'] + iterations = misc.get('iterations', 1) + G = nx.Graph(name=nodeset.name, + M=M, + VertexC=VertexC, + capacity=edgeset.capacity, + boundary=pickle.loads(nodeset.boundary), + landscape_angle=nodeset.landscape_angle, + funname=edgeset.method.funname, + edges_created_by=creator, + iterations=iterations) + + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + + D = edgeset.D or 0 + if D > 0: + G.graph['D'] = D + detournodes = range(N, N + D) + G.add_nodes_from(((s, {'type': 'detour'}) + for s in detournodes)) + clone2prime = edgeset.clone2prime + assert len(clone2prime) == D, \ + 'len(EdgeSet.clone2prime) != EdgeSet.D' + fnT = np.arange(N + D + M) + fnT[N: N + D] = clone2prime + fnT[-M:] = range(-M, 0) + G.graph['fnT'] = fnT + AllnodesC = np.vstack((VertexC[:N], + VertexC[clone2prime], + VertexC[-M:])) + else: + AllnodesC = VertexC + + edges = pickle.loads(edgeset.edges) + U, V = edges.T + Length = np.hypot(*(AllnodesC[U] - AllnodesC[V]).T) + G.add_weighted_edges_from(zip(U, V, Length), weight='length') + if D > 0: + for _, _, edgeD in G.edges(detournodes, data=True): + edgeD['type'] = 'detour' + G.graph['overfed'] = [len(G[root]) / np.ceil(N / edgeset.capacity) * M + for root in range(-M, 0)] + calc_length = G.size(weight='length') + assert abs(calc_length - edgeset.length) < 1, ( + f'{calc_length} != {edgeset.length}') + + if edgeset.method.options is not None: + G.graph['creation_options'] = json.loads(edgeset.method.options) + # make_graph_metrics(G) + calcload(G) + return G + + +def packnodes(G): + M = G.graph['M'] + N = G.number_of_nodes() - M + VertexCpkl = pickle.dumps(np.round(G.graph['VertexC'], 2)) + boundarypkl = pickle.dumps(np.round(G.graph['boundary'], 2)) + digest = sha256(VertexCpkl + boundarypkl).digest() + pack = dict( + digest=digest, + name=G.name, + N=N, + M=M, + VertexC=VertexCpkl, + boundary=boundarypkl, + landscape_angle=G.graph.get('landscape_angle', 0.), + ) + return pack + + +def packmethod(fun, options=None): + options = options or {} + if 'capacity' in options: + del options['capacity'] + funhash = sha256(fun.__code__.co_code).digest() + optionsstr = json.dumps(options) + digest = sha256(funhash + optionsstr.encode()).digest() + pack = dict( + digest=digest, + funname=fun.__name__, + funhash=funhash, + options=optionsstr, + ) + return pack + + +def add_if_absent(entity, pack): + digest = pack['digest'] + with db_session: + if not entity.exists(digest=digest): + entity(**pack) + return digest + + +def method_from_graph(G, db): + '''Returns primary key of the entry.''' + pack = packmethod(G.graph['edges_fun'], G.graph['creation_options']) + return add_if_absent(db.Method, pack) + + +def nodeset_from_graph(G, db): + '''Returns primary key of the entry.''' + pack = packnodes(G) + return add_if_absent(db.NodeSet, pack) + + +def edgeset_from_graph(G, db): + '''Adds a new EdgeSet entry in the database, using the data in `G`. + If the NodeSet or Method are not yet in the database, they will be added. + ''' + misc_not = {'VertexC', 'anglesYhp', 'anglesXhp', 'anglesRank', 'angles', + 'd2rootsRank', 'd2roots', 'name', 'boundary', 'capacity', + 'runtime', 'runtime_unit', 'edges_fun', 'D', 'DetourC', 'fnT', + 'crossings', 'landscape_angle'} + nodesetID = nodeset_from_graph(G, db) + methodID = method_from_graph(G, db), + machineID = get_machineID(db) + M = G.graph['M'] + edgepack = dict( + edges=pickle.dumps( + np.array([((u, v) if u < v else (v, u)) + for u, v in G.edges])), + length=G.size(weight='length'), + gates=[len(G[root]) for root in range(-M, 0)], + capacity=G.graph['capacity'], + runtime=G.graph['runtime'], + runtime_unit=G.graph['runtime_unit'], + misc=pickle.dumps({key: G.graph[key] + for key in G.graph.keys() - misc_not}), + ) + + D = G.graph.get('D') + if D is not None and D > 0: + N_plus_D = G.number_of_nodes() - M + assert len(G.graph['fnT']) == N_plus_D + M, \ + "len(fnT) != N + D + M" + edgepack['D'] = D + edgepack['clone2prime'] = G.graph['fnT'][N_plus_D - D: N_plus_D] + else: + edgepack['D'] = 0 + with db_session: + edgepack.update( + nodes=db.NodeSet[nodesetID], + method=db.Method[methodID], + machine=db.Machine[machineID], + ) + db.EdgeSet(**edgepack) + + +def get_machineID(db): + fqdn = getfqdn() + hostname = gethostname() + if fqdn == 'localhost': + machine = hostname + else: + if hostname.startswith('n-'): + machine = fqdn[len(hostname):] + else: + machine = fqdn + with db_session: + if not db.Machine.exists(name=machine): + newMachine = db.Machine(name=machine).id + return newMachine + else: + oldMachine = db.Machine.get(name=machine).id + return oldMachine + + +def G_by_method(G, method, db): + '''Fetch from the database a layout for `G` by `method`. + `G` must be a layout solution with the necessary info in the G.graph dict. + `method` is a Method. + ''' + farmname = G.name + c = G.graph['capacity'] + es = db.EdgeSet.get(lambda e: + e.nodes.name == farmname and + e.method is method and + e.capacity == c) + Gdb = graph_from_edgeset(es) + calcload(Gdb) + return Gdb + + +def Gs_from_attrs(farm, methods, capacities, db): + '''Fetch from the database a list of tuples of layouts. + (each tuple has one G for each of `methods`) + `farm` must have the desired NodeSet name in the `name` attribute. + `methods` is a tuple of Method instances. + `capacities` is an int or sequence thereof. + ''' + Gs = [] + if not isinstance(methods, Sequence): + methods = (methods,) + if not isinstance(capacities, Sequence): + capacities = (capacities,) + for c in capacities: + Gtuple = tuple( + graph_from_edgeset( + db.EdgeSet.get(lambda e: + e.nodes.name == farm.name and + e.method is m and + e.capacity == c)) + for m in methods) + for G in Gtuple: + calcload(G) + if len(Gtuple) == 1: + Gs.append(Gtuple[0]) + else: + Gs.append(Gtuple) + if len(Gs) == 1: + return Gs[0] + else: + return Gs diff --git a/ed_win/plotting_scripts/svg.py b/ed_win/plotting_scripts/svg.py new file mode 100644 index 0000000..efdad74 --- /dev/null +++ b/ed_win/plotting_scripts/svg.py @@ -0,0 +1,228 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +from collections import defaultdict + +import numpy as np + +import svg + +from .geometric import rotate +from .interarraylib import calcload + + +class SvgRepr(): + ''' + Helper class to get IPython to display the SVG figure encoded in data. + ''' + + def __init__(self, data: str): + self.data = data + + def _repr_svg_(self) -> str: + return self.data + + def save(self, filepath: str) -> None: + '''write SVG to file `filepath`''' + with open(filepath, 'w', encoding='utf-8') as file: + file.write(self.data) + + +def svgplot(G, landscape=True, dark=True, node_size=12): + '''NetworkX graph conversion to SVG''' + w, h = 1920, 1080 + margin = 30 + root_side = round(1.77 * node_size) + # TODO: ¿use SVG's attr overflow="visible" instead of margin? + VertexC = G.graph['VertexC'] + BoundaryC = G.graph.get('boundary') + if BoundaryC is None: + hull = G.graph.get('hull') + if hull is not None: + BoundaryC = VertexC[hull] + else: + import shapely as shp + BoundaryC = np.array(tuple(zip(*shp.MultiPoint( + G.graph['VertexC']).convex_hull.exterior.coords.xy))[:-1]) + landscape_angle = G.graph.get('landscape_angle') + if landscape and landscape_angle: + # landscape_angle is not None and not 0 + VertexC = rotate(VertexC, landscape_angle) + BoundaryC = rotate(BoundaryC, landscape_angle) + + # viewport scaling + Woff = min(VertexC[:, 0].min(), BoundaryC[:, 0].min()) + W = max(VertexC[:, 0].max(), BoundaryC[:, 0].max()) - Woff + Hoff = min(VertexC[:, 1].min(), BoundaryC[:, 1].min()) + H = max(VertexC[:, 1].max(), BoundaryC[:, 1].max()) - Hoff + wr = (w - 2 * margin) / W + hr = (h - 2 * margin) / H + if wr / hr < w / h: + r = wr + h = round(H * r + 2 * margin) + else: + r = hr + # w = round(W*r + 2*margin) + offset = np.array((Woff, Hoff)) + VertexS = (VertexC - offset) * r + margin + BoundaryS = (BoundaryC - offset) * r + margin + # y axis flipping + VertexS[:, 1] = h - VertexS[:, 1] + BoundaryS[:, 1] = h - BoundaryS[:, 1] + VertexS = VertexS.round().astype(int) + BoundaryS = BoundaryS.round().astype(int) + + # color settings + type2color = {} + type2style = dict( + detour='dashed', + scaffold='dotted', + extended='dashed', + delaunay='solid', + unspecified='solid', + ) + if dark: + type2color.update( + detour='darkorange', + scaffold='gray', + delaunay='darkcyan', + extended='darkcyan', + unspecified='crimson', + ) + root_color = 'lawngreen' + node_edge = 'none' + detour_ring = 'orange' + polygon_edge = '#333' + polygon_face = '#080808' + else: + type2color.update( + detour='royalblue', + scaffold='gray', + delaunay='black', + extended='black', + unspecified='firebrick', + ) + root_color = 'black' + node_edge = 'black' + detour_ring = 'deepskyblue' + polygon_edge = '#444444' + polygon_face = 'whitesmoke' + # matplotlib tab20 + colors = ('#1f77b4', '#aec7e8', '#ff7f0e', '#ffbb78', '#2ca02c', + '#98df8a', '#d62728', '#ff9896', '#9467bd', '#c5b0d5', + '#8c564b', '#c49c94', '#e377c2', '#f7b6d2', '#7f7f7f', + '#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf', '#9edae5') + + M = G.graph['M'] + N = VertexC.shape[0] - M + D = G.graph.get('D', 0) + if D: + fnT = G.graph['fnT'] + else: + fnT = [] + + # farm boundary shape + boundary = svg.Polygon( + id='boundary', + stroke=polygon_edge, + fill=polygon_face, + points=' '.join(str(c) for c in BoundaryS.flat) + ) + + if not G.graph.get('has_loads', False) and G.number_of_edges() == N + D: + calcload(G) + + # wtg nodes + subtrees = defaultdict(list) + for n, sub in G.nodes(data='subtree', default=19): + if 0 <= n < N: + subtrees[sub].append(n) + svgnodes = [] + for sub, nodes in subtrees.items(): + svgnodes.append(svg.G( + fill=colors[sub % len(colors)], + elements=[svg.Use(href='#wtg', x=VertexS[n, 0], y=VertexS[n, 1]) + for n in nodes])) + svgnodes = svg.G(id='WTGgrp', elements=svgnodes) + + # oss nodes + svgroots = svg.G( + id='OSSgrp', + elements=[svg.Use(href='#oss', x=VertexS[r, 0] - root_side / 2, + y=VertexS[r, 1] - root_side / 2) + for r in range(-M, 0)]) + # Detour nodes + svgdetours = svg.G( + id='DTgrp', elements=[svg.Use(href='#dt', x=VertexS[d, 0], + y=VertexS[d, 1]) for d in fnT[N: N + D]]) + + # Regular edges + class_dict = {'delaunay': 'del', 'extended': 'ext', None: 'std'} + edges_with_type = G.edges(data='type', default=None) + edge_lines = defaultdict(list) + for u, v, edge_type in edges_with_type: + if edge_type == 'detour': + continue + edge_lines[class_dict[edge_type]].append( + svg.Line(x1=VertexS[u, 0], y1=VertexS[u, 1], + x2=VertexS[v, 0], y2=VertexS[v, 1])) + edges = [svg.G(id='edges', class_=class_, elements=lines) + for class_, lines in edge_lines.items()] + # for class_, lines in edge_lines.items(): + # edges.append(svg.G(id='edges', class_=class_, elements=lines)) + # Detour edges as polylines (to align the dashes among overlapping lines) + Points = [] + for r in range(-M, 0): + detoured = [n for n in G.neighbors(r) if n >= N] + for t in detoured: + s = r + detour_hops = [s, fnT[t]] + while True: + nbr = set(G.neighbors(t)) + nbr.remove(s) + u = nbr.pop() + detour_hops.append(fnT[u]) + if u < N: + break + s, t = t, u + Points.append(' '.join(str(c) for c in VertexS[detour_hops].flat)) + if Points: + edgesdt = svg.G( + id='detours', class_='dt', + elements=[svg.Polyline(points=points) for points in Points]) + else: + edgesdt = [] + + # Defs (i.e. reusable elements) + def_elements = [] + wtg = svg.Circle(id='wtg', + stroke=node_edge, stroke_width=2, r=node_size) + def_elements.append(wtg) + oss = svg.Rect(id='oss', fill=root_color, stroke=node_edge, stroke_width=2, + width=root_side, height=root_side) + def_elements.append(oss) + detour = svg.Circle(id='dt', fill='none', stroke_opacity=0.3, + stroke=detour_ring, stroke_width=4, r=23) + def_elements.append(detour) + defs = svg.Defs(elements=def_elements) + + # Style + # TODO: use type2style below + style = svg.Style(text=( + f'polyline {{stroke-width: 4}} ' + f'line {{stroke-width: 4}} ' + f'.std {{stroke: {type2color["unspecified"]}}} ' + f'.del {{stroke: {type2color["delaunay"]}}} ' + f'.ext {{stroke: {type2color["extended"]}; stroke-dasharray: 18 15}} ' + f'.scf {{stroke: {type2color["scaffold"]}; stroke-dasharray: 10 10}} ' + f'.dt {{stroke-dasharray: 18 15; fill: none; ' + f'stroke: {type2color["detour"]}}}')) + + # Aggregate all elements in the SVG figure. + out = svg.SVG( + viewBox=svg.ViewBoxSpec(0, 0, w, h), + elements=[style, defs, + svg.G(id=G.graph['handle'], + elements=[boundary, *edges, edgesdt, svgnodes, + svgroots, svgdetours])]) + return SvgRepr(out.as_str()) diff --git a/ed_win/plotting_scripts/synthetic.py b/ed_win/plotting_scripts/synthetic.py new file mode 100644 index 0000000..31430a0 --- /dev/null +++ b/ed_win/plotting_scripts/synthetic.py @@ -0,0 +1,180 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import networkx as nx +import numpy as np +from scipy.spatial import ConvexHull + +from .geometric import make_graph_metrics +from .utils import NodeTagger + + +F = NodeTagger() + + +def toyfarm(): + VertexC = np.array([ + [49., 993.], # row 0 + [-145., 388.], # row 1 + [275., 562.], + [699., 566.], + [-371., -147.], # row 2 + [371., 109.], + [972., 206.], + [-585., -655.], # row 3 + [90., -475.], + [707., -244.], + [-104., -966.], # row 4 + [494., -772.], + [0., 0.], # OSS + ]) + BoundaryC = np.array([ + [49., 993.], # row 0 + [-145., 388.], # row 1 + [-371., -147.], # row 2 + [-585., -655.], # row 3 + [-104., -966.], # row 4 + [494., -772.], + [707., -244.], + [972., 206.], + [699., 566.], + ]) + M = 1 + N = VertexC.shape[0] - M + # create networkx graph + G = nx.Graph(M=M, + VertexC=VertexC, + boundary=BoundaryC, + name='toy', + handle='toy') + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + + make_graph_metrics(G) + return G + + +def synthfarm2graph(RootC, NodeC, BoundaryC=None, name='', handle='synthetic'): + N = NodeC.shape[0] + M = RootC.shape[0] + + # build data structures + VertexC = np.vstack((NodeC, RootC)) + if BoundaryC is None: + hull = ConvexHull(VertexC) + boundary = np.reshape(VertexC[hull.simplices], + (hull.simplices.shape[0] * 2, 2)) + # this arctan2() trick only works because the origin is in the middle + BoundaryC = boundary[np.argsort(np.arctan2(*boundary.T[::-1]))] + + # create networkx graph + G = nx.Graph(M=M, + VertexC=VertexC, + boundary=BoundaryC, + name=name, + handle=handle) + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + + make_graph_metrics(G) + return G + + +def equidistant(R, center='centroid', spacing=1): + ''' + Returns an array of coordinates for the vertices of a regular triangular + tiling (spacing sets the triangle's side) within radius R. + The coordinate origin is in the centroid of the central triangle. + ''' + lim = (R / spacing)**2 + h = np.sqrt(3) / 2 + + if center == 'centroid': + def iswithin(x, y): + return x**2 + y**2 <= lim + + Vsector = [] + + offset = np.sqrt(3) / 3 + i = 0 + repeat = True + # this loop fills a 120° sector + while True: + # x0 = (3*i + 2)*h/3 + x0 = i * h + offset + if i % 2 == 0 and repeat: + # add line starting at 0° + y0 = 0 + repeat = False + else: + # add line starting at 60° + y0 = x0 * h * 2 + repeat = True + i += 1 + if iswithin(x0, y0): + Vsector.append((x0, y0)) + c = 1 + while True: + x, y = x0 + c * h, y0 + c / 2 + if iswithin(x, y): + Vsector.append((x, y)) + r = np.sqrt(x**2 + y**2) + θ = 2 * np.pi / 3 - np.arctan2(y, x) + Vsector.append((r * np.cos(θ), r * np.sin(θ))) + else: + break + c += 1 + else: + if not repeat: + break + # replicate the 120° sector created to fill the circle + Vsector = np.array(Vsector) + r = np.hypot(*Vsector.T) + θ = np.arctan2(*Vsector.T[::-1]) + cos_sin = tuple(np.c_[np.cos(θ + β), np.sin(θ + β)] + for β in (2 * np.pi / 3, 4 * np.pi / 3)) + output = np.r_[tuple((Vsector,) + + tuple(cs * r[:, np.newaxis] for cs in cos_sin))] + + elif center == 'vertex': + def addupper(x, y): + X, Y = (x + 0.5, y + h) + if X**2 + Y**2 <= lim: + yield X, Y + yield from addupper(X, Y) + + def addlower(x, y): + X, Y = (x + 1, y) + if X**2 + Y**2 <= lim: + yield X, Y + yield from addlower(X, Y) + + def addbranches(x, y): + yield from addlower(x, y) + X, Y = (x + 1.5, y + h) + if X**2 + Y**2 <= lim: + yield X, Y + yield from addbranches(X, Y) + yield from addupper(x, y) + + firstbranch = (1.5, h) + Vsector = np.array(tuple(addlower(0, 0)) + + (firstbranch,) + + tuple(addbranches(*firstbranch))) + + # replicate the 60° sector created to fill the circle + Vsector = np.array(Vsector) + r = np.hypot(*Vsector.T) + θ = np.arctan2(*Vsector.T[::-1]) + cos_sin = tuple(np.c_[np.cos(θ + β), np.sin(θ + β)] + for β in np.pi / 3 * np.arange(1, 6)) + output = np.r_[tuple((np.zeros((1, 2), dtype=float),) + (Vsector,) + + tuple(cs * r[:, np.newaxis] for cs in cos_sin))] + else: + print('Unknown option for <center>:', center) + return None + return spacing * output diff --git a/ed_win/plotting_scripts/utils.py b/ed_win/plotting_scripts/utils.py new file mode 100644 index 0000000..fc724ca --- /dev/null +++ b/ed_win/plotting_scripts/utils.py @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import inspect +from collections import namedtuple + + +def namedtuplify(namedtuple_typename='', **kwargs): + NamedTuplified = namedtuple(namedtuple_typename, + tuple(str(kw) for kw in kwargs)) + return NamedTuplified(**kwargs) + + +class NodeTagger(): + # 50 digits, 'I' and 'l' were dropped + alphabet = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ' + value = {c: i for i, c in enumerate(alphabet)} + + def __getattr__(self, b50): + dec = 0 + digit_value = 1 + if b50[0] < 'α': + for digit in b50[::-1]: + dec += self.value[digit] * digit_value + digit_value *= 50 + return dec + else: + # for greek letters, only single digit is implemented + return ord('α') - ord(b50[0]) - 1 + + def __getitem__(self, dec): + if dec is None: + return '∅' + elif isinstance(dec, str): + return dec + b50 = [] + if dec >= 0: + while True: + dec, digit = divmod(dec, 50) + b50.append(self.alphabet[digit]) + if dec == 0: + break + return ''.join(b50[::-1]) + else: + return chr(ord('α') - dec - 1) + + +F = NodeTagger() + + +class NodeStr(): + + def __init__(self, fnT, N): + self.fnT = fnT + self.N = N + + def __call__(self, u, *args): + nodes = tuple((self.fnT[n], n) + for n in (u,) + args if n is not None) + out = '–'.join(F[n_] + ('' if n < self.N else f'({F[n]})') + for n_, n in nodes) + if len(nodes) > 1: + out = f'«{out}»' + else: + out = f'<{out}>' + return out + + +class Alerter(): + + def __init__(self, where, varname): + self.where = where + self.varname = varname + self.f_creation = inspect.stack()[1].frame + + def __call__(self, text): + i = self.f_creation.f_locals[self.varname] + function = inspect.stack()[1].function + if self.where(i, function): + print(f'[{i}|{function}] ' + text) diff --git a/ed_win/plotting_scripts/weighting.py b/ed_win/plotting_scripts/weighting.py new file mode 100644 index 0000000..56dec81 --- /dev/null +++ b/ed_win/plotting_scripts/weighting.py @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# https://github.com/mdealencar/interarray + +import numpy as np + + +class Weight(): + + @classmethod + def blockage_xtra(cls, data): + arc = data['arc'][data['root']] + penalty = np.pi / (np.pi - arc) + 4 * arc / np.pi + return data['length'] * penalty + + @classmethod + def blockage(cls, data): + arc = data['arc'][data['root']] + penalty = np.pi / (np.pi - arc) + return data['length'] * penalty diff --git a/ed_win/tests/test_wind_farm_network.py b/ed_win/tests/test_wind_farm_network.py index 0400f7d..83e40fb 100644 --- a/ed_win/tests/test_wind_farm_network.py +++ b/ed_win/tests/test_wind_farm_network.py @@ -1,82 +1,60 @@ import numpy as np -from ed_win.wind_farm_network import WindFarmNetwork, TwoStepHeuristicDriver +from ed_win.wind_farm_network import WindFarmNetwork import numpy.testing as npt -turbine_positions = np.asarray([[2000., 4000., 6000., - 8000., 498.65600569, 2498.65600569, 4498.65600569, - 6498.65600569, 8498.65600569, 997.31201137, 2997.31201137, - 4997.31201137, 11336.25662483, 8997.31201137, 1495.96801706, - 3495.96801706, 5495.96801706, 10011.39514341, 11426.89538545, - 1994.62402275, 3994.62402275, 5994.62402275, 7994.62402275, - 10588.90471566], - [0., 0., 0., - 0., 2000., 2000., 2000., - 2000., 2000., 4000., 4000., - 4000., 6877.42528387, 4000., 6000., - 6000., 6000., 3179.76530545, 5953.63051694, - 8000., 8000., 8000., 8000., - 4734.32972738]]) -substation_positions = np.asarray([[0], [0]]) -settings = {'option': 3, - 'Inters_const': True, - 'max_it': 20000} -cables = np.array([[500, 3, 100000], [800, 5, 150000], [1000, 10, 250000]]) -wfn = WindFarmNetwork(turbine_positions=turbine_positions, - substation_positions=substation_positions, - drivers=TwoStepHeuristicDriver(**settings), - cables=cables) +turbines_pos = np.asarray([[2000., 4000., 6000., + 8000., 498.65600569, 2498.65600569, 4498.65600569, + 6498.65600569, 8498.65600569, 997.31201137, 2997.31201137, + 4997.31201137, 11336.25662483, 8997.31201137, 1495.96801706, + 3495.96801706, 5495.96801706, 10011.39514341, 11426.89538545, + 1994.62402275, 3994.62402275, 5994.62402275, 7994.62402275, + 10588.90471566], + [0., 0., 0., + 0., 2000., 2000., 2000., + 2000., 2000., 4000., 4000., + 4000., 6877.42528387, 4000., 6000., + 6000., 6000., 3179.76530545, 5953.63051694, + 8000., 8000., 8000., 8000., + 4734.32972738]]).T +substations_pos = np.asarray([[0], [0]]).T -T_ref = np.asarray([[1.00000000e+00, 2.00000000e+00, 2.00000000e+03, 1.00000000e+00, - 3.00000000e+05], - [2.00000000e+00, 3.00000000e+00, 2.00000000e+03, 0.00000000e+00, - 2.00000000e+05], - [3.00000000e+00, 4.00000000e+00, 2.00000000e+03, 0.00000000e+00, - 2.00000000e+05], - [4.00000000e+00, 5.00000000e+00, 2.00000000e+03, 0.00000000e+00, - 2.00000000e+05], - [1.00000000e+00, 6.00000000e+00, 2.06122726e+03, 2.00000000e+00, - 5.15306815e+05], - [6.00000000e+00, 7.00000000e+00, 2.00000000e+03, 2.00000000e+00, - 5.00000000e+05], - [7.00000000e+00, 8.00000000e+00, 2.00000000e+03, 2.00000000e+00, - 5.00000000e+05], - [8.00000000e+00, 9.00000000e+00, 2.00000000e+03, 2.00000000e+00, - 5.00000000e+05], - [9.00000000e+00, 1.00000000e+01, 2.00000000e+03, 2.00000000e+00, - 5.00000000e+05], - [1.00000000e+01, 1.90000000e+01, 1.91839148e+03, 1.00000000e+00, - 2.87758722e+05], - [1.90000000e+01, 1.50000000e+01, 1.30428124e+03, 0.00000000e+00, - 1.30428124e+05], - [1.90000000e+01, 2.50000000e+01, 1.65836903e+03, 0.00000000e+00, - 1.65836903e+05], - [2.50000000e+01, 2.00000000e+01, 1.47950085e+03, 0.00000000e+00, - 1.47950085e+05], - [2.00000000e+01, 1.40000000e+01, 9.28230659e+02, 0.00000000e+00, - 9.28230659e+04], - [1.00000000e+00, 1.10000000e+01, 4.12245452e+03, 2.00000000e+00, - 1.03061363e+06], - [1.10000000e+01, 1.20000000e+01, 2.00000000e+03, 0.00000000e+00, - 2.00000000e+05], - [1.20000000e+01, 1.30000000e+01, 2.00000000e+03, 0.00000000e+00, - 2.00000000e+05], - [1.10000000e+01, 1.60000000e+01, 2.06122726e+03, 2.00000000e+00, - 5.15306815e+05], - [1.60000000e+01, 1.70000000e+01, 2.00000000e+03, 0.00000000e+00, - 2.00000000e+05], - [1.70000000e+01, 1.80000000e+01, 2.00000000e+03, 0.00000000e+00, - 2.00000000e+05], - [1.60000000e+01, 2.10000000e+01, 2.06122726e+03, 1.00000000e+00, - 3.09184089e+05], - [2.10000000e+01, 2.20000000e+01, 2.00000000e+03, 0.00000000e+00, - 2.00000000e+05], - [2.20000000e+01, 2.30000000e+01, 2.00000000e+03, 0.00000000e+00, - 2.00000000e+05], - [2.30000000e+01, 2.40000000e+01, 2.00000000e+03, 0.00000000e+00, - 2.00000000e+05]]) +cables = np.array([[500, 3, 100], [800, 5, 150], [1000, 10, 250]]) +wfn = WindFarmNetwork(turbines_pos=turbines_pos, substations_pos=substations_pos, cables=cables) + +T_ref = [(2, 1, 2000., 4, 1, 300000.), + (2, 3, 2000., 3, 0, 200000.), + (3, 4, 2000., 2, 0, 200000.), + (4, 5, 2000., 1, 0, 200000.), + (6, 1, 2061.2272587, 10, 2, 515306.81467517), + (6, 7, 2000., 9, 2, 500000.), + (7, 8, 2000., 8, 2, 500000.), + (8, 9, 2000., 7, 2, 500000.), + (9, 10, 2000., 6, 2, 500000.), + (10, 19, 1918.39148109, 5, 1, 287758.72216407), + (11, 1, 4122.4545174, 10, 2, 1030613.62934973), + (11, 16, 2061.2272587, 7, 2, 515306.81467517), + (11, 12, 2000., 2, 0, 200000.), + (12, 13, 2000., 1, 0, 200000.), + (14, 20, 928.23065901, 1, 0, 92823.06590142), + (15, 19, 1304.28123993, 1, 0, 130428.12399293), + (16, 21, 2061.2272587, 4, 1, 309184.0888051), + (16, 17, 2000., 2, 0, 200000.), + (17, 18, 2000., 1, 0, 200000.), + (19, 25, 1658.3690325, 3, 0, 165836.90325048), + (20, 25, 1479.50085437, 2, 0, 147950.08543684), + (21, 22, 2000., 3, 0, 200000.), + (22, 23, 2000., 2, 0, 200000.), + (23, 24, 2000., 1, 0, 200000.)] def test_wind_farm_network(): - cost, state = wfn.design() - assert 7495208.24825092 == cost - npt.assert_allclose(wfn.T, T_ref) + Table_wfn = wfn.optimize(output_format='table') + T_wfn = [tuple(row) for row in Table_wfn] + H = wfn.optimize() + H.plot() + cost = H.size(weight='cost') + assert 7495208 == round(cost) + npt.assert_allclose(T_wfn, T_ref) + substations_pos_new = np.asarray([[499620.7], [5711622.0]]).T + H2 = wfn.evaluate(substations_pos=substations_pos, update_selfG=True) + H2.plot() diff --git a/ed_win/wind_farm_network.py b/ed_win/wind_farm_network.py index d4909bb..5f01fd6 100644 --- a/ed_win/wind_farm_network.py +++ b/ed_win/wind_farm_network.py @@ -1,9 +1,23 @@ +import matplotlib.pyplot as plt +import yaml from abc import ABC, abstractmethod -from ed_win.collection_system import collection_system -from ed_win.c_mst_cables import plot_network -# from ed_win.repair import repair +# from ed_win.collection_system import collection_system +# from ed_win.c_mst_cables import plot_network +from ed_win.plotting import gplot import pandas as pd import numpy as np +import networkx as nx +import os +import xarray as xr +from windIO.utils.yml_utils import load_yaml, Loader +from ed_win.drivers.drivers_api import InterArray +from ed_win.drivers.interarray.interface import assign_cables +from ed_win.drivers.interarray.utils import NodeTagger +from ed_win.edwin_lib import make_graph_metrics, check_crossings, is_graph_connected, is_inside_boundary, graph_to_table, graph_to_yaml +from ed_win.drivers.interarray.interarraylib import calcload +from ed_win.drivers.ga.GA import GA + +F = NodeTagger() class Driver(ABC): @@ -11,18 +25,12 @@ class Driver(ABC): ''' ''' - def run(self, x=None, y=None, T=None): + def run(self, G=None): ''' - x : array-like - concatenated array of sub-station and turbine x-coordinates - y : array-like - concatenated array of sub-station and turbine y-coordinates - T : array-like - solution tree - + G: Networkx graph ''' - T, cables_cost = self._run(x=x, y=y, T=T) - return T, cables_cost + G = self._run(G=G) + return G @abstractmethod def _run(): @@ -31,184 +39,272 @@ class Driver(ABC): ''' -class Repair(Driver): - def __init__(self, **kwargs): - self.supports_constraints = False - self.supports_primary = False - self.supports_secondary = True - - Driver.__init__(self, **kwargs) - - def _run(self, T, **kwargs): - print('I will repair T') - cost = self.wfn.cost - return T, cost - - -class Refine(Driver): - def __init__(self, **kwargs): - self.supports_constraints = False - self.supports_primary = False - self.supports_secondary = True - - Driver.__init__(self, **kwargs) - - def _run(self, T, **kwargs): - print('I will refine T') - cost = self.wfn.cost - return T, cost - - -class TwoStepHeuristicDriver(Driver): - def __init__(self, option=3, Inters_const=True, max_it=20000, **kwargs): +class InterArrayDriver(Driver): + def __init__(self, solver_name='heuristic(cpew)', gap=0.01, timelimit=600, other_settings={}, **kwargs): + self.solver_name = solver_name + self.gap = gap + self.timelimit = timelimit + self.other_settings = other_settings self.supports_constraints = False self.supports_primary = True self.supports_secondary = False - - self.option = option - self.Inters_const = Inters_const - self.max_it = max_it Driver.__init__(self, **kwargs) - def _run(self, x, y, **kwargs): - T, cables_cost = collection_system(x, - y, - self.option, - self.Inters_const, - self.max_it, - self.wfn.cables) - return T, cables_cost + def _run(self, G, **kwargs): + H = InterArray(G, + self.solver_name, + self.gap, + self.timelimit, + self.other_settings, + self.wfn.cables).H + return H -class GlobalDriver(Driver): - def __init__(self, option=3, Inters_const=True, max_it=20000, **kwargs): - self.supports_constraints = True +class GeneticAlgorithmDriver(Driver): + def __init__(self, WindFarm=None, settings={}, verbose=False, **kwargs): + self.WindFarm = WindFarm + self.settings = settings + self.verbose = verbose + self.supports_constraints = False self.supports_primary = True - self.supports_secondary = True - - self.option = option - self.Inters_const = Inters_const - self.max_it = max_it + self.supports_secondary = False Driver.__init__(self, **kwargs) - def _run(self, x, y, T, **kwargs): - T, cables_cost = collection_system(x, - y, - self.option, - self.Inters_const, - self.max_it, - self.wfn.cables) - return T, cables_cost + def _run(self, G, **kwargs): + H = GA(G, wf=self.WindFarm, settings=self.settings, verbose=self.verbose).H + return H -class GeneticAlgorithmDriver(Driver): - def __init__(self, option=3, Inters_const=True, max_it=20000, **kwargs): - self.supports_constraints = True - self.supports_primary = True - self.supports_secondary = True - self.option = option - self.Inters_const = Inters_const - self.max_it = max_it - Driver.__init__(self, **kwargs) - - def _run(self, x, y, T, **kwargs): - T, cables_cost = collection_system(x, - y, - self.option, - self.Inters_const, - self.max_it, - self.wfn.cables) - return T, cables_cost - - -class WindFarmNetwork(): - def __init__(self, turbine_positions, substation_positions, drivers=[TwoStepHeuristicDriver()], cables=[], T=None, sequence=None): - """WindFarmNetwork object - - Parameters - ---------- - turbine_positions : array-like - Two dimentional array with turbine x- and y-coordinates - substation_positions : array-like - Two dimentional array with sub station x- and y-coordinates - drivers : list - List of Driver objects - cables : array-like - The shape of the array is (n, m), where n is the number of available cables and m is 3. - m=1 is cross-section, m=2 is the allowed number of connected WTs and m=3 is the price/km of the cable - """ - if not isinstance(drivers, list): - drivers = [drivers] - self.turbine_positions = turbine_positions - self.substation_positions = substation_positions - self.drivers = drivers +class WindFarmNetwork(nx.Graph): + def __init__(self, turbines_pos=None, substations_pos=None, drivers=None, cables=[], site_info={}, G=None, sequence=None): + """WindFarmNetwork object""" + super().__init__() # Initialize as a NetworkX graph + if turbines_pos is not None and substations_pos is not None: + self.G = self._create_graph(turbines_pos, substations_pos, cables, site_info) + else: + self.G = G + self.drivers = drivers or [InterArrayDriver(solver_name='heuristic(cpew)', other_settings={})] self.cables = cables - self.state = None - self.T = T - self.columns = ['from_node', 'to_node', 'cable_length', 'cable_type', 'cable_cost'] - if isinstance(sequence, type(None)): - sequence = range(len(drivers)) - self.sequence = sequence + self.sequence = sequence or range(len(self.drivers)) self.setup() + @classmethod + def from_windIO(cls, yaml_path, drivers=None, cables=None, sequence=None): + """Create WindFarmNetwork instance from windIO YAML file.""" + windIO_data = cls._load_windIO_data(yaml_path) + turbines_pos, substations_pos = cls._extract_positions(windIO_data) + site_info = cls._extract_site_info(windIO_data) + G = cls._create_graph(turbines_pos, substations_pos, cables, site_info) + return cls(G=G, drivers=drivers, cables=cables, sequence=sequence) + + @classmethod + def from_graph(cls, graph, drivers=None, cables=None, G=None, sequence=None): + """Create WindFarmNetwork instance from a NetworkX graph.""" + return cls(G=graph, drivers=drivers, cables=cables, sequence=sequence) + + @staticmethod + def _load_windIO_data(yaml_path): + """Load windIO YAML data.""" + + # Custom constructor for netCDF data + def includeBathymetryNetCDF(self, node): + filename = os.path.join(self._root, self.construct_scalar(node)) + dataset = xr.open_dataset(filename) + bathymetry_data = {variable: list(dataset[variable].values.reshape(-1)) + for variable in dataset.variables} + return bathymetry_data + + Loader.includeBathymetryNetCDF = includeBathymetryNetCDF + Loader.add_constructor('!includeBathymetryNetCDF', + Loader.includeBathymetryNetCDF) + + if not os.path.exists(yaml_path): + raise FileNotFoundError(f"YAML file '{yaml_path}' not found.") + return load_yaml(yaml_path, Loader) + + @staticmethod + def _extract_positions(windIO_data): + """Extract turbine and substation positions from windIO data.""" + try: + turbines_x = windIO_data['wind_farm']['layouts']['initial_layout']['coordinates']['x'] + turbines_y = windIO_data['wind_farm']['layouts']['initial_layout']['coordinates']['y'] + turbines_pos = np.array([(x, y) for x, y in zip(turbines_x, turbines_y)]) + + substations_x = windIO_data['wind_farm']['electrical_substations']['coordinates']['x'] + substations_y = windIO_data['wind_farm']['electrical_substations']['coordinates']['y'] + substations_pos = np.column_stack((substations_x, substations_y)) + except KeyError as e: + raise ValueError(f"Required data '{e.args[0]}' not found in YAML file.") + return turbines_pos, substations_pos + + @classmethod + def _extract_site_info(cls, windIO_data): + """Extract site information from windIO data.""" + site_data = windIO_data.get('site', {}) + site_name = site_data.get('name', None) + handle = site_data.get('handle', None) + boundary_data = site_data.get('boundaries', {}).get('polygons', None) + boundary = np.array([[x, y] for x, y in zip(boundary_data[0]['x'], boundary_data[1]['y'])]) if boundary_data else None + return {'site_name': site_name, 'handle': handle, 'boundary': boundary} + + @staticmethod + def _create_graph(turbines_pos, substations_pos, cables, site_info={}): + """Create a network graph from turbine and substation positions.""" + M = substations_pos.shape[0] + VertexC = np.r_[turbines_pos, substations_pos[::-1]] + N = len(VertexC) - M + + # Set default site information if not provided + site_info = site_info + site_info.setdefault('handle', 'G_from_site') + site_info.setdefault('name', site_info.get('handle')) + + # Create the network graph + G = nx.Graph( + M=M, + VertexC=VertexC, + boundary=site_info.get('boundary'), + name=site_info['name'], + handle=site_info['handle'], + cables={'area': cables[:, 0], 'capacity': cables[:, 1], 'cost': cables[:, 2]} + ) + + # Add nodes for turbines and substations + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) + for n in range(N))) + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) + for r in range(-M, 0))) + + return G + def setup(self): + """Set up drivers.""" for driver in self.drivers: setattr(driver, 'wfn', self) - def design(self, turbine_positions=None, T=None, **kwargs): - """designs or optimizes the electrical wind farm network - - Parameters - ---------- - turbine_positions : array-like - Two dimentional array with turbine x- and y-coordinates - - T : array - The current network tree with the columns f{self.columns} - - Returns - ------- - cost : float - The cost of the electrical network - T : array - The current network tree with the columns f{self.columns} - """ - if not isinstance(turbine_positions, type(None)): - self.turbine_positions = turbine_positions - if isinstance(T, type(None)): - T = self.T - - x, y = self.positions_to_xy() + def optimize(self, turbines_pos=None, substations_pos=None, drivers=None, cables=[], site_info={}, G=None, sequence=None, output_format='graph', + **kwargs): + """Design the wind farm network.""" + G = G if G is not None else self.G + if turbines_pos is not None or substations_pos is not None: + M = substations_pos.shape[0] if substations_pos is not None else G.graph['M'] + turbines_pos = turbines_pos if turbines_pos is not None else G.graph['VertexC'][0:-M] + substations_pos = substations_pos if substations_pos is not None else G.graph['VertexC'][-M:][::-1] + VertexC = np.r_[turbines_pos, substations_pos[::-1]] + G.graph['VertexC'] = VertexC + G.graph['M'] = M + N = len(VertexC) - M + # Remove previously added nodes for turbines and substations + nodes_to_remove = [n for n in G if 'type' in G.nodes[n] and G.nodes[n]['type'] in ['wtg', 'oss']] + G.remove_nodes_from(nodes_to_remove) + + # Add new nodes for turbines + G.add_nodes_from(((n, {'label': F[n], 'type': 'wtg'}) for n in range(N))) + + # Add new nodes for substations + G.add_nodes_from(((r, {'label': F[r], 'type': 'oss'}) for r in range(-M, 0))) + + if cables: + G.graph['cables'] = {'area': cables[:, 0], 'capacity': cables[:, 1], 'cost': cables[:, 2]} + if site_info: + G.graph['site_info'].update(site_info) + if sequence is not None: + self.sequence = sequence + + if drivers is not None: + self.drivers = drivers + + if G.graph['boundary'] is not None: + inside_boundary = is_inside_boundary(boundary=G.graph['boundary'], coordinates=G.graph['VertexC']) + if not inside_boundary: + print('WARNING: at least one of the coordinates is outside the boundary') for n, driver_no in enumerate(self.sequence): driver = self.drivers[driver_no] if n == 0 and not driver.supports_primary: - raise Exception(driver + ' cannot be the first driver in a sequence') + raise ValueError(f"{driver} cannot be the first driver in a sequence") elif n > 0 and not driver.supports_secondary: - raise Exception(driver + ' cannot be used as a secondary driver') - T, cost = driver.run(x=x, y=y, T=T) - self.T = T - self.cost = cost - - return cost, T - - def positions_to_xy(self): - return [np.concatenate((self.substation_positions[i], self.turbine_positions[i]), axis=0) for i in [0, 1]] - - def tree_as_table(self): - tree_table = pd.DataFrame(self.T, columns=self.columns) - tree_table = tree_table.astype({'from_node': int, - 'to_node': int, - 'cable_type': int}) - return tree_table - - def get_edges(self, x, y): - return 'edges' - - def plot(self): - x, y = self.positions_to_xy() - plot_network(x, y, self.cables, self.T) + raise ValueError(f"{driver} cannot be used as a secondary driver") + G = driver.run(G=G) + self.G = G + + # Attach a plot method to the graph object + def plot_graph(node_tag=None): + ax = gplot(G, node_tag=node_tag) + return ax + + G.plot = plot_graph + + if output_format == 'graph': + output = G + elif output_format == 'table': + output = graph_to_table(G) + elif output_format == 'yaml': + output = graph_to_yaml(G) + else: + raise ValueError("Invalid output format. Supported formats: 'graph', 'table', 'yaml'") + return output + + def evaluate(self, turbines_pos=None, substations_pos=None, output_format='graph', update_selfG=False, **kwargs): + """Evaluates the layout with new coordinates.""" + G = self.G.copy() + M = substations_pos.shape[0] if substations_pos is not None else G.graph['M'] + + # Check if the graph has any edges + if len(G.edges) == 0: + raise ValueError("The graph (G) is empty. \n" + "Either run the optimize() before calling evaluate or " + " instantiate the class with a graph (G) which includes a layout (at least one edge)") + + # + if not is_graph_connected(G): + print('WARNING: the layout has some disconned nodes!') + + # Check if the number of turbines and substations matches with the graph + if turbines_pos is not None and len(turbines_pos) != len(G.graph['VertexC']) - M: + raise ValueError("Number of turbines in input parameters does not match with the graph.") + if substations_pos is not None and len(substations_pos) != M: + raise ValueError("Number of substations in input parameters does not match with the graph.") + + turbines_pos = turbines_pos if turbines_pos is not None else G.graph['VertexC'][0:-M] + substations_pos = substations_pos if substations_pos is not None else G.graph['VertexC'][-M:][::-1] + vertexes = np.r_[turbines_pos, substations_pos[::-1]] + G.graph['VertexC'] = vertexes + make_graph_metrics(G) + for u, v, edgeD in G.edges(data=True): + # Calculate new edge length based on updated coordintes + edgeD['length'] = ((vertexes[v][0] - vertexes[u][0])**2 + (vertexes[v][1] - vertexes[u][1])**2)**0.5 + edgeD['cost'] = edgeD['length'] * G.graph['cables']['cost'][edgeD['cable']] + + edge_crossing = check_crossings(G) + if edge_crossing: + print('WARNING: there is at least one cable crossing with the new coordinates') + + if G.graph['boundary'] is not None: + inside_boundary = is_inside_boundary(boundary=G.graph['boundary'], coordinates=G.graph['VertexC']) + if not inside_boundary: + print('WARNING: at least one of the coordinates is outside the boundary') + + self.G_evaluate = G + if update_selfG: + self.G = G + + # Attach a plot method to the graph object + def plot_graph(node_tag=None): + ax = gplot(G, node_tag=node_tag) + return ax + G.plot = plot_graph + + if output_format == 'graph': + output = G + elif output_format == 'table': + output = graph_to_table(G) + elif output_format == 'yaml': + output = graph_to_yaml(G) + else: + raise ValueError("Invalid output format. Supported formats: 'graph', 'table', 'yaml'") + return output class Constraints(dict): @@ -222,35 +318,68 @@ class Constraints(dict): def main(): if __name__ == '__main__': - turbine_positions = np.asarray([[2000., 4000., 6000., - 8000., 498.65600569, 2498.65600569, 4498.65600569, - 6498.65600569, 8498.65600569, 997.31201137, 2997.31201137, - 4997.31201137, 11336.25662483, 8997.31201137, 1495.96801706, - 3495.96801706, 5495.96801706, 10011.39514341, 11426.89538545, - 1994.62402275, 3994.62402275, 5994.62402275, 7994.62402275, - 10588.90471566], - [0., 0., 0., - 0., 2000., 2000., 2000., - 2000., 2000., 4000., 4000., - 4000., 6877.42528387, 4000., 6000., - 6000., 6000., 3179.76530545, 5953.63051694, - 8000., 8000., 8000., 8000., - 4734.32972738]]) - substation_positions = np.asarray([[0], [0]]) - settings = {'option': 3, - 'Inters_const': True, - 'max_it': 20000, - 'repair': True} - cables = np.array([[500, 3, 100000], [800, 5, 150000], [1000, 10, 250000]]) - wfn = WindFarmNetwork(turbine_positions=turbine_positions, - substation_positions=substation_positions, - drivers=[TwoStepHeuristicDriver(**settings), Refine(), Repair()], - sequence=[0, 2, 1], - cables=cables) - cost, state = wfn.design() - wfn.plot() - print(wfn.tree_as_table()) - print(cost) + # Turbine and substation positions from IEA37 Borssele Regular System + substations_pos = np.asarray([[497620.7], [5730622.0]]).T + turbines_pos = np.array([[500968.1461, 499748.6565, 501245.7744, 500026.2848, + 498527.8286, 497308.339, 501523.4027, 500303.9131, + 498805.4569, 497585.9673, 496087.5111, 494868.0215, + 501801.031, 500581.5414, 499083.0852, 497863.5956, + 496365.1394, 495145.6498, 493647.1936, 492427.704, + 502078.6593, 500859.1697, 499360.7135, 498141.2239, + 496642.7677, 495423.2781, 493924.8219, 492705.3323, + 491206.8762, 489987.3865, 502356.2876, 501136.798, + 499638.3418, 498418.8522, 496920.396, 495700.9064, + 494202.4502, 492982.9606, 491484.5045, 490265.0148, + 488766.5587, 487547.069, 502633.9159, 501414.4263, + 499915.9701, 498696.4805, 497198.0243, 495978.5347, + 494480.0786, 493260.5889, 491762.1328, 490542.6431, + 489044.187, 487824.6973, 486326.2412, 485106.7515, + 497475.6526, 496256.163, 494757.7069, 493538.2172, + 492039.7611, 490820.2714, 489321.8153, 488102.3256, + 486603.8695, 497753.2809, 496533.7913, 495035.3352, + 493815.8455, 492317.3894, 489599.4436, 498030.9093, + 496811.4196, 495312.9635], + [5716452.784, 5717635.848, 5718427.22, 5719610.283, + 5718809.394, 5719992.458, 5720401.656, 5721584.719, + 5720783.83, 5721966.894, 5721166.004, 5722349.068, + 5722376.092, 5723559.155, 5722758.266, 5723941.33, + 5723140.44, 5724323.504, 5723522.615, 5724705.678, + 5724350.528, 5725533.591, 5724732.702, 5725915.765, + 5725114.876, 5726297.94, 5725497.051, 5726680.114, + 5725879.225, 5727062.288, 5726324.963, 5727508.027, + 5726707.138, 5727890.201, 5727089.312, 5728272.376, + 5727471.486, 5728654.55, 5727853.661, 5729036.724, + 5728235.835, 5729418.899, 5728299.399, 5729482.463, + 5728681.574, 5729864.637, 5729063.748, 5730246.812, + 5729445.922, 5730628.986, 5729828.097, 5731011.16, + 5730210.271, 5731393.335, 5730592.445, 5731775.509, + 5731038.184, 5732221.248, 5731420.358, 5732603.422, + 5731802.533, 5732985.596, 5732184.707, 5733367.77, + 5732566.881, 5733012.62, 5734195.683, 5733394.794, + 5734577.858, 5733776.968, 5734159.143, 5734987.056, + 5736170.119, 5735369.23]]).T + BoundaryC = np.array(list(zip([484178.55, 500129.9, 497318.1, + 503163.37, 501266.5, 488951.0], + [5732482.8, 5737534.4, 5731880.24, + 5729155.3, 5715990.05, 5727940.]))) + site_info = {'site_name': 'IEA-37 Regular', + 'handle': 'iea37reg', + 'boundary': BoundaryC + } + cables = np.array([[500, 3, 206], [800, 5, 287], [1000, 7, 406]]) + + wfn = WindFarmNetwork(turbines_pos=turbines_pos, substations_pos=substations_pos, cables=cables, site_info=site_info) + + # Optimize cable layout with the given data + H1 = wfn.optimize(output_format='table') + print(H1) + H1.plot() + + # Evaluate the layout with new coordinates + new_substations_pos = np.asarray([[499620.7], [5731622.0]]).T + H2 = wfn.evaluate(substations_pos=new_substations_pos, update_selfG=True) + H2.plot() + plt.show() main() diff --git a/setup.py b/setup.py index 0899fed..8244a21 100644 --- a/setup.py +++ b/setup.py @@ -9,9 +9,9 @@ import pkg_resources repo = os.path.dirname(__file__) try: from git_utils import write_vers - version = write_vers(vers_file='ed_win/__init__.py', repo=repo, skip_chars=1) + version = '1.1.0' # write_vers(vers_file='ed_win/__init__.py', repo=repo, skip_chars=1) except Exception: - version = '999' + version = '999.0.0' try: @@ -30,14 +30,17 @@ setup(name='ed_win', long_description=read_md('README.md'), url='https://gitlab.windenergy.dtu.dk/TOPFARM/EDWIN', author='DTU Wind Energy', - author_email='juru@dtu.dk', + author_email='amia@dtu.dk', license='MIT', - packages=find_packages(), + packages=find_packages() + ['ed_win.drivers'], install_requires=[ + 'networkx', # plotting 'matplotlib', # for plotting + 'shapely', # for geometric calculus 'numpy', # for numerical calculations 'xarray', # for WaspGridSite data storage 'scipy', # constraints + 'windIO', ], extras_require={ 'test': [ @@ -45,5 +48,16 @@ setup(name='ed_win', 'pytest-cov', # for calculating coverage 'sphinx', # generating documentation 'sphinx_rtd_theme', # docs theme + ], + 'interarray': [ + 'openpyxl', + 'pony', + 'pyyaml', + 'utm', + 'pyomo', + 'loguru', + 'numba', + 'ortools', + 'svg.py', ]}, zip_safe=True) -- GitLab