├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── environment.yml ├── lib ├── ADMM.py ├── FlowGNN.py ├── README.md ├── __init__.py ├── config.py ├── graph_utils.py ├── path_utils.py ├── teal_actor.py ├── teal_env.py ├── teal_model.py └── utils.py ├── requirements.txt ├── run ├── teal.py └── teal_helper.py ├── topologies ├── ASN2k.json ├── B4.json ├── Kdl.json ├── UsCarrier.json └── paths │ └── path-form │ ├── B4.json-4-paths_edge-disjoint-True_dist-metric-min-hop-dict.pkl │ └── UsCarrier.json-4-paths_edge-disjoint-True_dist-metric-min-hop-dict.pkl └── traffic-matrices ├── real ├── B4.json_real_0_1.0_traffic-matrix.pkl ├── B4.json_real_10_1.0_traffic-matrix.pkl ├── B4.json_real_11_1.0_traffic-matrix.pkl ├── B4.json_real_12_1.0_traffic-matrix.pkl ├── B4.json_real_13_1.0_traffic-matrix.pkl ├── B4.json_real_14_1.0_traffic-matrix.pkl ├── B4.json_real_15_1.0_traffic-matrix.pkl ├── B4.json_real_16_1.0_traffic-matrix.pkl ├── B4.json_real_17_1.0_traffic-matrix.pkl ├── B4.json_real_18_1.0_traffic-matrix.pkl ├── B4.json_real_19_1.0_traffic-matrix.pkl ├── B4.json_real_1_1.0_traffic-matrix.pkl ├── B4.json_real_20_1.0_traffic-matrix.pkl ├── B4.json_real_21_1.0_traffic-matrix.pkl ├── B4.json_real_22_1.0_traffic-matrix.pkl ├── B4.json_real_23_1.0_traffic-matrix.pkl ├── B4.json_real_24_1.0_traffic-matrix.pkl ├── B4.json_real_25_1.0_traffic-matrix.pkl ├── B4.json_real_26_1.0_traffic-matrix.pkl ├── B4.json_real_27_1.0_traffic-matrix.pkl ├── B4.json_real_28_1.0_traffic-matrix.pkl ├── B4.json_real_29_1.0_traffic-matrix.pkl ├── B4.json_real_2_1.0_traffic-matrix.pkl ├── B4.json_real_30_1.0_traffic-matrix.pkl ├── B4.json_real_31_1.0_traffic-matrix.pkl ├── B4.json_real_32_1.0_traffic-matrix.pkl ├── B4.json_real_33_1.0_traffic-matrix.pkl ├── B4.json_real_34_1.0_traffic-matrix.pkl ├── B4.json_real_35_1.0_traffic-matrix.pkl ├── B4.json_real_3_1.0_traffic-matrix.pkl ├── B4.json_real_4_1.0_traffic-matrix.pkl ├── B4.json_real_5_1.0_traffic-matrix.pkl ├── B4.json_real_6_1.0_traffic-matrix.pkl ├── B4.json_real_7_1.0_traffic-matrix.pkl ├── B4.json_real_8_1.0_traffic-matrix.pkl └── B4.json_real_9_1.0_traffic-matrix.pkl └── toy ├── ASN2k.json_toy_0_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_10_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_11_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_12_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_13_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_14_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_15_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_16_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_17_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_18_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_19_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_1_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_20_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_21_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_22_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_23_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_24_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_25_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_26_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_27_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_28_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_29_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_2_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_30_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_31_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_32_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_33_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_34_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_35_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_3_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_4_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_5_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_6_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_7_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_8_1.0_traffic-matrix.pkl ├── ASN2k.json_toy_9_1.0_traffic-matrix.pkl ├── Kdl.json_toy_0_1.0_traffic-matrix.pkl ├── Kdl.json_toy_10_1.0_traffic-matrix.pkl ├── Kdl.json_toy_11_1.0_traffic-matrix.pkl ├── Kdl.json_toy_12_1.0_traffic-matrix.pkl ├── Kdl.json_toy_13_1.0_traffic-matrix.pkl ├── Kdl.json_toy_14_1.0_traffic-matrix.pkl ├── Kdl.json_toy_15_1.0_traffic-matrix.pkl ├── Kdl.json_toy_16_1.0_traffic-matrix.pkl ├── Kdl.json_toy_17_1.0_traffic-matrix.pkl ├── Kdl.json_toy_18_1.0_traffic-matrix.pkl ├── Kdl.json_toy_19_1.0_traffic-matrix.pkl ├── Kdl.json_toy_1_1.0_traffic-matrix.pkl ├── Kdl.json_toy_20_1.0_traffic-matrix.pkl ├── Kdl.json_toy_21_1.0_traffic-matrix.pkl ├── Kdl.json_toy_22_1.0_traffic-matrix.pkl ├── Kdl.json_toy_23_1.0_traffic-matrix.pkl ├── Kdl.json_toy_24_1.0_traffic-matrix.pkl ├── Kdl.json_toy_25_1.0_traffic-matrix.pkl ├── Kdl.json_toy_26_1.0_traffic-matrix.pkl ├── Kdl.json_toy_27_1.0_traffic-matrix.pkl ├── Kdl.json_toy_28_1.0_traffic-matrix.pkl ├── Kdl.json_toy_29_1.0_traffic-matrix.pkl ├── Kdl.json_toy_2_1.0_traffic-matrix.pkl ├── Kdl.json_toy_30_1.0_traffic-matrix.pkl ├── Kdl.json_toy_31_1.0_traffic-matrix.pkl ├── Kdl.json_toy_32_1.0_traffic-matrix.pkl ├── Kdl.json_toy_33_1.0_traffic-matrix.pkl ├── Kdl.json_toy_34_1.0_traffic-matrix.pkl ├── Kdl.json_toy_35_1.0_traffic-matrix.pkl ├── Kdl.json_toy_3_1.0_traffic-matrix.pkl ├── Kdl.json_toy_4_1.0_traffic-matrix.pkl ├── Kdl.json_toy_5_1.0_traffic-matrix.pkl ├── Kdl.json_toy_6_1.0_traffic-matrix.pkl ├── Kdl.json_toy_7_1.0_traffic-matrix.pkl ├── Kdl.json_toy_8_1.0_traffic-matrix.pkl ├── Kdl.json_toy_9_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_0_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_10_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_11_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_12_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_13_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_14_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_15_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_16_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_17_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_18_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_19_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_1_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_20_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_21_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_22_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_23_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_24_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_25_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_26_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_27_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_28_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_29_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_2_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_30_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_31_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_32_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_33_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_34_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_35_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_3_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_4_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_5_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_6_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_7_1.0_traffic-matrix.pkl ├── UsCarrier.json_toy_8_1.0_traffic-matrix.pkl └── UsCarrier.json_toy_9_1.0_traffic-matrix.pkl /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | # path folders 163 | topologies/paths/ncflow-edge-per-iter/ 164 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pop-ncflow-lptop"] 2 | path = pop-ncflow-lptop 3 | url = https://github.com/harvard-cns/pop-ncflow-lptop 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Harvard Cloud Networking and Systems Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Teal: Traffic Engineering Accelerated by Learning 2 | 3 | [Teal](https://dl.acm.org/doi/10.1145/3603269.3604857) is a learning-accelerated traffic engineering (TE) algorithm for cloud wide-area networks (WANs), published at ACM SIGCOMM '23. 4 | By harnessing the parallel processing power of GPUs, Teal achieves unprecedented 5 | acceleration of TE control, surpassing production TE solvers by several orders of magnitude 6 | while retaining near-optimal flow allocations. 7 | 8 | ## Getting started 9 | 10 | ### Hardware requirements 11 | 12 | - Linux OS (tested on Ubuntu 20.04, 22.04, and CentOS 7) 13 | - A CPU instance with 16+ cores 14 | - (Optional\*) A GPU instance with 24+ GB memory and CUDA installed 15 | 16 | \*The baseline TE schemes only require a CPU to run. Teal runs on CPU as well, but its runtime will be significantly longer than on GPU. 17 | 18 | ### Cloning Teal with submodules 19 | - `git clone https://github.com/harvard-cns/teal.git` 20 | - `cd teal` and update git submodules with `git submodule update --init --recursive` 21 | 22 | ### Dependencies 23 | - Run `conda env create -f environment.yml` to create a Conda environment with essential Python dependencies 24 | - [Miniconda](https://docs.anaconda.com/free/anaconda/install/index.html) or [Anaconda](https://docs.anaconda.com/free/anaconda/install/index.html) is required 25 | - Run `conda activate teal` to activate the Conda environment. All the following steps related to Python (e.g., `pip` and `python` commands) **must be performed within this Conda environment** to ensure correct Python dependencies. 26 | - Run `pip install -r requirements.txt` to install additional Python dependencies 27 | 28 | #### Dependencies only required for baselines 29 | - Install `make` 30 | - e.g., `sudo apt install build-essential` on Ubuntu 31 | - Acquire a Gurobi license from [Gurobi](https://www.gurobi.com/solutions/licensing/) and activate it with `grbgetkey [gurobi-license]` 32 | - Run `gurobi_cl` to verify the activation 33 | 34 | #### Dependencies only required for Teal 35 | - If on a GPU instance, run `nvcc --version` to identify the installed version of CUDA 36 | - Note: when following the next steps to install `torch`, `torch-scatter`, and `torch-sparse`, it might be fine to select a version that supports a different CUDA version than the output of `nvcc`, provided that this CUDA version is supported by the GPU driver (as shown in `nvidia-smi`). 37 | - Follow the [official instructions](https://pytorch.org/get-started/previous-versions/) to install PyTorch via pip based on the execution environment (CPU, or GPU with a specific version of CUDA). 38 | - *Example:* Install PyTorch 1.10.1 for CUDA 11.1 on a **GPU** instance: 39 | ``` 40 | pip install torch==1.10.1+cu111 torchvision==0.11.2+cu111 torchaudio==0.10.1 -f https://download.pytorch.org/whl/cu111/torch_stable.html 41 | ``` 42 | Run `python -c "import torch; print(torch.cuda.is_available())"` to verify the installation. 43 | - *Example:* Install PyTorch 1.10.1 on a **CPU** instance: 44 | ``` 45 | pip install torch==1.10.1+cpu torchvision==0.11.2+cpu torchaudio==0.10.1 -f https://download.pytorch.org/whl/cpu/torch_stable.html 46 | ``` 47 | Run `python -c "import torch; print(torch.__version__)"` to verify the installation. 48 | - Install PyTorch extension libraries `torch-scatter` and `torch-sparse`: 49 | - First, identify the appropriate archive URL [here](https://data.pyg.org/whl/) based on PyTorch and CUDA versions. E.g., copy the link of `torch-1.10.1+cu111` for PyTorch 1.10.1 and CUDA 11.1. 50 | - Run `pip install --no-index torch-scatter torch-sparse -f [archive URL]`, replacing `[archive URL]` with the copied archive URL. 51 | - *Example:* On a **GPU** instance with PyTorch 1.10.1 and CUDA 11.1: 52 | ``` 53 | pip install --no-index torch-scatter torch-sparse -f https://data.pyg.org/whl/torch-1.10.1%2Bcu111.html` 54 | ``` 55 | - *Example:* On a **CPU** instance with PyTorch 1.10.1: 56 | ``` 57 | pip install --no-index torch-scatter torch-sparse -f https://data.pyg.org/whl/torch-1.10.1%2Bcpu.html 58 | ``` 59 | - Run `python -c "import torch_scatter; print(torch_scatter.__version__)"` and `python -c "import torch_sparse; print(torch_sparse.__version__)"` to verify the installation. 60 | - Troubleshooting: refer to the [Installation from Source section](https://pytorch-geometric.readthedocs.io/en/latest/install/installation.html#installation-from-source). 61 | 62 | ## Code structure 63 | ``` 64 | . 65 | ├── lib # source code for Teal (details in lib/README.md) 66 | ├── pop-ncflow-lptop # submodule for baselines 67 | │ ├── benchmarks # test code for baselines 68 | │ ├── ext # external code for baselines 69 | │ └── lib # source code for baselines 70 | ├── run # test code for Teal 71 | ├── topologies # network topologies with link capacity (e.g. `B4.json`) 72 | │ └── paths # paths in topologies (auto-generated if not existent) 73 | └── traffic-matrices # TE traffic matrices 74 | ├── real # real traffic matrices from abilene.txt in Yates (https://github.com/cornell-netlab/yates) 75 | │ # (e.g. `B4.json_real_0_1.0_traffic-matrix.pkl`) 76 | └── toy # toy traffic matrices (e.g. `ASN2k.json_toy_0_1.0_traffic-matrix.pkl`) 77 | ``` 78 | 79 | **Note:** As we are not allowed to share the proprietary traffic data from Microsoft WAN (or the Teal model trained on that data), we mapped the publicly accessible Yates traffic data to the B4 topology to facilitate code testing. For the other topologies (UsCarrier, Kdl, and ASN), we synthetically generated "toy" traffic matrices due to their larger sizes. 80 | 81 | ## Evaluating Teal 82 | To evaluate Teal on the B4 topology: 83 | ``` 84 | $ cd ./run 85 | $ python teal.py --obj total_flow --topo B4.json --epochs 3 --admm-steps 2 86 | Loading paths from pickle file ~/teal/topologies/paths/path-form/B4.json-4-paths_edge-disjoint-True_dist-metric-min-hop-dict.pkl 87 | path_dict size: 132 88 | Creating model teal-models/B4.json_flowGNN-6_std-False.pt 89 | Training epoch 0/3: 100%|█████████████████████████████████| 1/1 [00:01<00:00, 1.63s/it] 90 | Training epoch 1/3: 100%|█████████████████████████████████| 1/1 [00:00<00:00, 2.45it/s] 91 | Training epoch 2/3: 100%|█████████████████████████████████| 1/1 [00:00<00:00, 2.61it/s] 92 | Testing: 100%|████████████████| 8/8 [00:00<00:00, 38.06it/s, runtime=0.0133, obj=0.9537] 93 | ``` 94 | 95 | To show explanations on the input parameters: 96 | ``` 97 | $ python teal.py --help 98 | ``` 99 | 100 | Results will be saved in 101 | - `teal-total_flow-all.csv`: performance numbers 102 | - `teal-logs`: directory with TE solution matrices 103 | - `teal-models`: directory to save the trained models when `--model-save True` 104 | 105 | Realistic traffic matrices are only available for B4 (please refer to the [note](#code-structure) above). For the other topologies — UsCarrier (`UsCarrier.json`), Kdl (`Kdl.json`), or ASN (`ASN2k.json`), use the "toy" traffic matrices we generated (taking UsCarrier as an example): 106 | ``` 107 | $ python teal.py --obj total_flow --topo UsCarrier.json --tm-model toy --epochs 3 --admm-steps 2 108 | ``` 109 | 110 | ## Evaluating baselines 111 | Teal is compared with the following baselines: 112 | - LP-all (`path_form.py`): LP-all solves the TE optimization problem for *all* demands using linear programming (implemented in Gurobi) 113 | - LP-top (`top_form.py`): LP-top allocates the *top* α% (α=10 by default) of demands using an LP solver and assigns the remaining demands to the shortest paths 114 | - NCFlow (`ncflow.py`): the NCFlow algorithm from the NSDI '21 paper: [*Contracting Wide-area Network Topologies to Solve Flow Problems Quickly*](https://www.usenix.org/conference/nsdi21/presentation/abuzaid) 115 | - POP (`pop.py`): the POP algorithm from the SOSP '21 paper: [*Solving Large-Scale Granular Resource Allocation Problems Efficiently with POP*](https://dl.acm.org/doi/10.1145/3477132.3483588) 116 | 117 | To evaluate the baselines on B4, run the following commands from the project root: 118 | ``` 119 | $ cd ./pop-ncflow-lptop/benchmarks 120 | $ python path_form.py --obj total_flow --topos B4.json 121 | $ python top_form.py --obj total_flow --topos B4.json 122 | $ python ncflow.py --obj total_flow --topos B4.json 123 | $ python pop.py --obj total_flow --topos B4.json --algo-cls PathFormulation --split-fractions 0.25 --num-subproblems 4 124 | ``` 125 | Results will be saved in 126 | - `path-form-total_flow-all.csv`, `top-form-total_flow-all.csv`, `ncflow-total_flow-all.csv`, `pop-total_flow-all.csv`: performance numbers 127 | - `path-form-logs`, `top-form-logs`, `ncflow-logs`, `pop-logs`: directory with TE solution matrices 128 | 129 | To test on UsCarrier (`UsCarrier.json`), Kdl (`Kdl.json`), or ASN (`ASN2k.json`), specify the "toy" traffic matrices we generated (taking UsCarrier as an example): 130 | ``` 131 | $ python path_form.py --obj total_flow --tm-models toy --topos UsCarrier.json 132 | $ python top_form.py --obj total_flow --tm-models toy --topos UsCarrier.json 133 | $ python ncflow.py --obj total_flow --tm-models toy --topos UsCarrier.json 134 | $ python pop.py --obj total_flow --tm-models toy --topos UsCarrier.json --algo-cls PathFormulation --split-fractions 0.25 --num-subproblems 4 135 | ``` 136 | 137 | ## Extending Teal 138 | 139 | To add another TE implementation to this repo, 140 | 141 | - If the implementation is based on linear programming or Gurobi, add test code to `./pop-ncflow-lptop/benchmarks/` and source code to `./pop-ncflow-lptop/lib/algorithms`. Code in `./pop-ncflow-lptop/lib` (e.g., `lp_solver.py`, `traffic_matrix.py`) and `./pop-ncflow-lptop/benchmarks` (e.g., `benchmark_helpers.py`) is reusable. 142 | - If the implementation is based on machine learning, add test code to `./run/` and source code to `./lib/`. Code in `./lib/` (e.g., `teal_env.py`, `utils.py`) and `./run/` (e.g., `teal_helpers.py`) is reusable. 143 | 144 | 145 | ## Citation 146 | If you use our code in your research, please cite our paper: 147 | ``` 148 | @inproceedings{teal, 149 | title={Teal: Learning-Accelerated Optimization of WAN Traffic Engineering}, 150 | author={Xu, Zhiying and Yan, Francis Y. and Singh, Rachee and Chiu, Justin T. and Rush, Alexander M. and Yu, Minlan}, 151 | booktitle={Proceedings of the ACM SIGCOMM 2023 Conference}, 152 | pages={378--393}, 153 | month=sep, 154 | year={2023} 155 | } 156 | ``` 157 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: teal 2 | channels: 3 | - gurobi 4 | - https://conda.anaconda.org/gurobi 5 | - defaults 6 | dependencies: 7 | - _libgcc_mutex=0.1=main 8 | - appdirs=1.4.4=py_0 9 | - argon2-cffi=20.1.0=py38h27cfd23_1 10 | - astroid=2.5=py38h06a4308_1 11 | - async_generator=1.10=pyhd3eb1b0_0 12 | - attrs=20.3.0=pyhd3eb1b0_0 13 | - backcall=0.2.0=pyhd3eb1b0_0 14 | - black=19.10b0=py_0 15 | - blas=1.0=mkl 16 | - bleach=3.3.0=pyhd3eb1b0_0 17 | - ca-certificates=2021.4.13=h06a4308_1 18 | - certifi=2020.12.5=py38h06a4308_0 19 | - cffi=1.14.5=py38h261ae71_0 20 | - click=7.1.2=pyhd3eb1b0_0 21 | - cycler=0.10.0=py38_0 22 | - dbus=1.13.18=hb2f20db_0 23 | - decorator=4.4.2=pyhd3eb1b0_0 24 | - defusedxml=0.7.1=pyhd3eb1b0_0 25 | - entrypoints=0.3=py38_0 26 | - expat=2.3.0=h2531618_2 27 | - fontconfig=2.13.1=h6c09931_0 28 | - freetype=2.10.4=h5ab3b9f_0 29 | - glib=2.68.1=h36276a3_0 30 | - gst-plugins-base=1.14.0=h8213a91_2 31 | - gstreamer=1.14.0=h28cd5cc_2 32 | - gurobi=9.1.2=py38_0 33 | - icu=58.2=he6710b0_3 34 | - importlib-metadata=3.10.0=py38h06a4308_0 35 | - importlib_metadata=3.10.0=hd3eb1b0_0 36 | - intel-openmp=2021.2.0=h06a4308_610 37 | - ipykernel=5.3.4=py38h5ca1d4c_0 38 | - ipython=7.22.0=py38hb070fc8_0 39 | - ipython_genutils=0.2.0=pyhd3eb1b0_1 40 | - ipywidgets=7.6.3=pyhd3eb1b0_1 41 | - isort=5.8.0=pyhd3eb1b0_0 42 | - jedi=0.17.0=py38_0 43 | - jinja2=2.11.3=pyhd3eb1b0_0 44 | - joblib=1.0.1=pyhd3eb1b0_0 45 | - jpeg=9b=h024ee3a_2 46 | - jsonschema=3.2.0=py_2 47 | - jupyter=1.0.0=py38_7 48 | - jupyter_client=6.1.12=pyhd3eb1b0_0 49 | - jupyter_console=6.4.0=pyhd3eb1b0_0 50 | - jupyter_core=4.7.1=py38h06a4308_0 51 | - jupyterlab_pygments=0.1.2=py_0 52 | - jupyterlab_widgets=1.0.0=pyhd3eb1b0_1 53 | - kiwisolver=1.3.1=py38h2531618_0 54 | - lazy-object-proxy=1.6.0=py38h27cfd23_0 55 | - lcms2=2.12=h3be6417_0 56 | - ld_impl_linux-64=2.33.1=h53a641e_7 57 | - libffi=3.3=he6710b0_2 58 | - libgcc-ng=9.1.0=hdf63c60_0 59 | - libgfortran-ng=7.3.0=hdf63c60_0 60 | - libpng=1.6.37=hbc83047_0 61 | - libsodium=1.0.18=h7b6447c_0 62 | - libstdcxx-ng=9.1.0=hdf63c60_0 63 | - libtiff=4.1.0=h2733197_1 64 | - libuuid=1.0.3=h1bed415_2 65 | - libxcb=1.14=h7b6447c_0 66 | - libxml2=2.9.10=hb55368b_3 67 | - lz4-c=1.9.3=h2531618_0 68 | - markupsafe=1.1.1=py38h7b6447c_0 69 | - matplotlib=3.3.4=py38h06a4308_0 70 | - matplotlib-base=3.3.4=py38h62a2d02_0 71 | - mccabe=0.6.1=py38_1 72 | - mistune=0.8.4=py38h7b6447c_1000 73 | - mkl=2021.2.0=h06a4308_296 74 | - mkl-service=2.3.0=py38h27cfd23_1 75 | - mkl_fft=1.3.0=py38h42c9631_2 76 | - mkl_random=1.2.1=py38ha9443f7_2 77 | - mypy_extensions=0.4.3=py38_0 78 | - nbclient=0.5.3=pyhd3eb1b0_0 79 | - nbconvert=6.0.7=py38_0 80 | - nbformat=5.1.3=pyhd3eb1b0_0 81 | - ncurses=6.2=he6710b0_1 82 | - nest-asyncio=1.5.1=pyhd3eb1b0_0 83 | - networkx=2.5.1=pyhd3eb1b0_0 84 | - notebook=6.3.0=py38h06a4308_0 85 | - numpy=1.20.1=py38h93e21f0_0 86 | - numpy-base=1.20.1=py38h7d8b39e_0 87 | - olefile=0.46=py_0 88 | - openssl=1.1.1k=h27cfd23_0 89 | - packaging=20.9=pyhd3eb1b0_0 90 | - pandas=1.2.4=py38h2531618_0 91 | - pandoc=2.12=h06a4308_0 92 | - pandocfilters=1.4.3=py38h06a4308_1 93 | - parso=0.8.2=pyhd3eb1b0_0 94 | - pathspec=0.7.0=py_0 95 | - pcre=8.44=he6710b0_0 96 | - pexpect=4.8.0=pyhd3eb1b0_3 97 | - pickleshare=0.7.5=pyhd3eb1b0_1003 98 | - pillow=8.2.0=py38he98fc37_0 99 | - pip=21.0.1=py38h06a4308_0 100 | - prometheus_client=0.10.1=pyhd3eb1b0_0 101 | - prompt-toolkit=3.0.17=pyh06a4308_0 102 | - prompt_toolkit=3.0.17=hd3eb1b0_0 103 | - ptyprocess=0.7.0=pyhd3eb1b0_2 104 | - pycparser=2.20=py_2 105 | - pygments=2.8.1=pyhd3eb1b0_0 106 | - pylint=2.7.4=py38h06a4308_1 107 | - pyparsing=2.4.7=pyhd3eb1b0_0 108 | - pyqt=5.9.2=py38h05f1152_4 109 | - pyrsistent=0.17.3=py38h7b6447c_0 110 | - python=3.8.8=hdb3f193_5 111 | - python-dateutil=2.8.1=pyhd3eb1b0_0 112 | - pytz=2021.1=pyhd3eb1b0_0 113 | - pyzmq=20.0.0=py38h2531618_1 114 | - qt=5.9.7=h5867ecd_1 115 | - qtconsole=5.0.3=pyhd3eb1b0_0 116 | - qtpy=1.9.0=py_0 117 | - readline=8.1=h27cfd23_0 118 | - regex=2021.4.4=py38h27cfd23_0 119 | - rope=0.18.0=py_0 120 | - scikit-learn=0.24.1=py38ha9443f7_0 121 | - scipy=1.6.2=py38had2a1c9_1 122 | - seaborn=0.11.1=pyhd3eb1b0_0 123 | - send2trash=1.5.0=pyhd3eb1b0_1 124 | - setuptools=52.0.0=py38h06a4308_0 125 | - sip=4.19.13=py38he6710b0_0 126 | - six=1.15.0=py38h06a4308_0 127 | - sqlite=3.35.4=hdfb4753_0 128 | - terminado=0.9.4=py38h06a4308_0 129 | - testpath=0.4.4=pyhd3eb1b0_0 130 | - threadpoolctl=2.1.0=pyh5ca1d4c_0 131 | - tk=8.6.10=hbc83047_0 132 | - toml=0.10.2=pyhd3eb1b0_0 133 | - tornado=6.1=py38h27cfd23_0 134 | - traitlets=5.0.5=pyhd3eb1b0_0 135 | - typed-ast=1.4.2=py38h27cfd23_1 136 | - typing_extensions=3.7.4.3=pyha847dfd_0 137 | - wcwidth=0.2.5=py_0 138 | - webencodings=0.5.1=py38_1 139 | - wheel=0.36.2=pyhd3eb1b0_0 140 | - widgetsnbextension=3.5.1=py38_0 141 | - wrapt=1.12.1=py38h7b6447c_1 142 | - xz=5.2.5=h7b6447c_0 143 | - zeromq=4.3.4=h2531618_0 144 | - zipp=3.4.1=pyhd3eb1b0_0 145 | - zlib=1.2.11=h7b6447c_3 146 | - zstd=1.4.9=haebb681_0 147 | -------------------------------------------------------------------------------- /lib/ADMM.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import torch 4 | import torch_scatter 5 | 6 | 7 | class ADMM(): 8 | """Fine-tunes the allocations and mitigates constraint violations. 9 | F1_d = d - sum_{p in e} x_p - s1 for demand d; 10 | F3_e = c - sum_{pe in e} z_pe - s3 for edge e; 11 | F4_pe = x_p - z_pe for path p edge e; 12 | The augumented Lagranian for TE is 13 | L(x, z, s, lambda) 14 | = - sum_d sum_p x_p 15 | + lambda1 * F1 + lambda3 * F3 + lambda4 * F4 16 | + rho/2 * F1^2 + rho/2 * F3^2 + rho/2 * F4^2. 17 | """ 18 | 19 | def __init__( 20 | self, p2e, num_path, num_path_node, num_edge_node, 21 | rho, device): 22 | """Initialize ADMM with the network topology. 23 | 24 | Args: 25 | teal_env: teal environment 26 | rho: hyperparameter for the augumented Lagranian 27 | device: device for new tensor to be allocated 28 | """ 29 | 30 | self.rho = rho 31 | self.device = device 32 | 33 | self.p2e = p2e 34 | self.num_path = num_path 35 | self.num_path_node = num_path_node 36 | self.num_edge_node = num_edge_node 37 | 38 | self.A_d_inv = self.init_A() 39 | self.b_extra = torch.zeros(self.num_edge_node).double().to(self.device) 40 | self.p2e_1_extra = torch.arange(self.num_edge_node).to(self.device) 41 | 42 | def init_A(self): 43 | """Return the inverse of A_d. 44 | A_d = diag(num_edges_for_paths) + 1 for demand d. 45 | """ 46 | 47 | num_edges_for_path = torch_scatter.scatter( 48 | torch.ones(self.p2e.shape[1]).to(self.device), 49 | self.p2e[0]) 50 | A_d_inv = torch.stack( 51 | [torch.linalg.inv(torch.diag(ele) + 1) for ele 52 | in num_edges_for_path.reshape(-1, self.num_path)]) 53 | 54 | return A_d_inv 55 | 56 | def update_obs_action(self, obs, action): 57 | """Update demands, capacity, allocation.""" 58 | 59 | # update demands and capacity 60 | self.d = obs[-self.num_path_node::self.num_path] 61 | self.c = obs[:-self.num_path_node] 62 | 63 | # update allocation in path-wise and edge-wise 64 | self.x = action 65 | self.z = self.x[self.p2e[0]] 66 | 67 | # init slack variables and lambda 68 | self.s1, self.s3 = 0, 0 69 | self.l1, self.l3, self.l4 = 0, 0, 0 70 | 71 | def update_admm(self): 72 | """Update ADMM for one round.""" 73 | 74 | self.update_s() 75 | self.update_lambda() 76 | self.update_z() 77 | self.update_x() 78 | 79 | def update_s(self): 80 | """Update slack variables s = argmin_s L(x, z, s, lambda). 81 | s1 = - lambda1 / rho + (d - sum x_p) 82 | s3 = - lambda3 / rho + (c - sum z_pe) 83 | """ 84 | 85 | self.s1 = self.l1 / self.rho \ 86 | + (self.d - self.x.reshape(-1, self.num_path).sum(1)) 87 | self.s3 = self.l3 / self.rho \ 88 | + (self.c - torch_scatter.scatter(self.z, self.p2e[1])) 89 | 90 | self.s1 = self.s1.relu() 91 | self.s3 = self.s3.relu() 92 | 93 | def update_x(self): 94 | """Update x = argmin_x L(x, z, s, lambda). 95 | x = - A_d_inv * b_d, 96 | where [b_d]_p = - 1 - lambda1_d + sum_{e in p} lambda4_pe 97 | + rho * (- d + s1_d) - rho * sum_{e in p} z_pe. 98 | """ 99 | 100 | b = -1 - self.l1[:, None] \ 101 | + self.rho*(-self.d + self.s1)[:, None]\ 102 | + torch_scatter.scatter( 103 | self.l4-self.rho*self.z, 104 | self.p2e[0]).reshape(-1, self.num_path) 105 | 106 | self.x = -torch.einsum( 107 | "nab,nb->na", 108 | self.A_d_inv/self.rho, 109 | b).reshape(-1) 110 | 111 | # use x.relu() to approximate the non-negative solution 112 | self.x = self.x.relu() 113 | 114 | def update_z(self, num_approx=1): 115 | """Update z = argmin_z L(x, z, s, lambda). 116 | z = - A_e_inv * b_e, 117 | where [b_e]_p = - lambda3_e - lambda4_pe 118 | + rho * (- c_e + s_e - x_p). 119 | where A_e = I + 1. 120 | 121 | Args: 122 | num_approx: num of approx rounds for the non-negative solution 123 | """ 124 | 125 | p2e_1 = self.p2e[1].clone() 126 | 127 | # 'double' precision is necessary: 128 | # torch_scatter is implemented via atomic operations on the GPU and is 129 | # therefore **non-deterministic** since the order of parallel 130 | # operations to the same value is undetermined. 131 | # For floating-point variables, this results in a source of variance in 132 | # the result. 133 | b = ( 134 | self.rho*( 135 | -self.c[self.p2e[1]] + self.s3[self.p2e[1]] 136 | - self.x[self.p2e[0]]) 137 | - self.l3[self.p2e[1]] - self.l4 138 | ).double() 139 | 140 | # z = - A_e_inv * b_e = sum b_e / (|b_e| + 1) - b 141 | # use b_extra and p2e_1_extra for |b_e| + 1 142 | b_mean = torch_scatter.scatter( 143 | torch.concat([b, self.b_extra]), 144 | torch.concat([p2e_1, self.p2e_1_extra]), reduce='mean') 145 | self.z = (b_mean[self.p2e[1]] - b)/self.rho 146 | 147 | # cannot use x.relu() to approximate the non-negative solution 148 | # iteratively decide which z is 0 and solve the rest of z 149 | for _ in range(num_approx): 150 | p2e_1[self.z < 0] = self.num_edge_node 151 | b_mean = torch_scatter.scatter( 152 | torch.concat([b, self.b_extra]), 153 | torch.concat([p2e_1, self.p2e_1_extra]), reduce='mean') 154 | self.z = (b_mean[self.p2e[1]] - b)/self.rho 155 | self.z = self.z.float().relu() 156 | 157 | def update_lambda(self): 158 | """Update lambda. 159 | lambda1 = lambda1 + rho * (d - sum_{p in e} x_p - s1); 160 | lambda3 = lambda3 + rho * (c - sum_{pe in e} z_pe - s3); 161 | lambda4 = lambda4 + rho * (x_p - z_pe). 162 | """ 163 | 164 | self.l1 = self.l1 + self.rho * ( 165 | self.d - self.x.reshape(-1, self.num_path).sum(1) - self.s1) 166 | self.l3 = self.l3 + self.rho * ( 167 | self.c - torch_scatter.scatter(self.z, self.p2e[1]) - self.s3) 168 | self.l4 = self.l4 + self.rho * ( 169 | self.x[self.p2e[0]] - self.z) 170 | 171 | def tune_action(self, obs, action, num_admm_step): 172 | """Return fine-tuned allocations after ADMM. 173 | 174 | Args: 175 | obs: observation (capacity + traffic matrix) 176 | action: action to correct 177 | num_admm_step: number of admm steps 178 | """ 179 | # init x, z, s, lambda 180 | self.update_obs_action(obs, action) 181 | 182 | # admm steps 183 | for _ in range(num_admm_step): 184 | self.update_admm() 185 | 186 | return self.x 187 | -------------------------------------------------------------------------------- /lib/FlowGNN.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | import torch 4 | import torch.nn as nn 5 | import torch_scatter 6 | import torch_sparse 7 | 8 | from .utils import weight_initialization 9 | 10 | 11 | class FlowGNN(nn.Module): 12 | """Transform the demands into compact feature vectors known as embeddings. 13 | 14 | FlowGNN alternates between 15 | - GNN layers aimed at capturing capacity constraints; 16 | - DNN layers aimed at capturing demand constraints. 17 | 18 | Replace torch_sparse package with torch_geometric pakage is possible 19 | but require larger memory space. 20 | """ 21 | 22 | def __init__(self, teal_env, num_layer): 23 | """Initialize flowGNN with the network topology. 24 | 25 | Args: 26 | teal_env: teal environment 27 | num_layer: num of layers in flowGNN 28 | """ 29 | 30 | super(FlowGNN, self).__init__() 31 | 32 | self.env = teal_env 33 | self.num_layer = num_layer 34 | 35 | self.edge_index = self.env.edge_index 36 | self.edge_index_values = self.env.edge_index_values 37 | self.num_path = self.env.num_path 38 | self.num_path_node = self.env.num_path_node 39 | self.num_edge_node = self.env.num_edge_node 40 | # self.adj_adj = torch.sparse_coo_tensor(self.edge_index, 41 | # self.edge_index_values, 42 | # [self.num_path_node + self.num_edge_node, 43 | # self.num_path_node + self.num_edge_node]) 44 | 45 | self.gnn_list = [] 46 | self.dnn_list = [] 47 | for i in range(self.num_layer): 48 | # to replace with GCNConv package: 49 | # self.gnn_list.append(GCNConv(i+1, i+1)) 50 | self.gnn_list.append(nn.Linear(i+1, i+1)) 51 | self.dnn_list.append( 52 | nn.Linear(self.num_path*(i+1), self.num_path*(i+1))) 53 | self.gnn_list = nn.ModuleList(self.gnn_list) 54 | self.dnn_list = nn.ModuleList(self.dnn_list) 55 | 56 | # weight initialization for dnn and gnn 57 | self.apply(weight_initialization) 58 | 59 | def forward(self, h_0): 60 | """Return embeddings after forward propagation 61 | 62 | Args: 63 | h_0: inital embeddings 64 | """ 65 | 66 | h_i = h_0 67 | for i in range(self.num_layer): 68 | 69 | # gnn 70 | # to replace with GCNConv package: 71 | # h_i = self.gnn_list[i](h_i, self.edge_index) 72 | h_i = self.gnn_list[i](h_i) 73 | # h_i = torch.sparse.mm(self.adj_adj, h_i) 74 | h_i = torch_sparse.spmm( 75 | self.edge_index, self.edge_index_values, 76 | h_0.shape[0], h_0.shape[0], h_i) 77 | 78 | # dnn 79 | h_i_path_node = self.dnn_list[i]( 80 | h_i[-self.num_path_node:, :].reshape( 81 | self.num_path_node//self.num_path, 82 | self.num_path*(i+1)))\ 83 | .reshape(self.num_path_node, i+1) 84 | h_i = torch.concat( 85 | [h_i[:-self.num_path_node, :], h_i_path_node], axis=0) 86 | 87 | # skip connection 88 | h_i = torch.cat([h_i, h_0], axis=-1) 89 | 90 | # return path-node embeddings 91 | return h_i[-self.num_path_node:, :] 92 | -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | ## Code structure 2 | ``` 3 | ./lib 4 | ├── init.py # package initialization when imported 5 | ├── config.py # directory configurations 6 | ├── teal_env.py # traffic engineering environment 7 | ├── teal_model.py # Teal model for training, validation, and testing 8 | ├── FlowGNN.py # FlowGNN: extracts flow features 9 | ├── teal_actor.py # multi-agent RL: maps flow features to initial allocations 10 | ├── ADMM.py # ADMM: fine-tunes allocations to satisfy constraints 11 | ├── graph_utils.py # utility functions for graph processing 12 | ├── path_util.py # utility functions for path processing 13 | └── util.py # utility functions for Teal, e.g. printing results 14 | ``` 15 | -------------------------------------------------------------------------------- /lib/__init__.py: -------------------------------------------------------------------------------- 1 | from .ADMM import ADMM 2 | from .config import TL_DIR, TOPOLOGIES_DIR, TM_DIR 3 | from .FlowGNN import FlowGNN 4 | from .graph_utils import path_to_edge_list 5 | from .path_utils import find_paths, graph_copy_with_edge_weights, remove_cycles 6 | from .teal_actor import TealActor 7 | from .teal_env import TealEnv 8 | from .teal_model import Teal 9 | from .utils import weight_initialization, uni_rand, print_ 10 | -------------------------------------------------------------------------------- /lib/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | TL_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), "..")) 4 | TOPOLOGIES_DIR = os.path.join(TL_DIR, "topologies") 5 | TM_DIR = os.path.join(TL_DIR, "traffic-matrices") 6 | -------------------------------------------------------------------------------- /lib/graph_utils.py: -------------------------------------------------------------------------------- 1 | from .utils import uni_rand 2 | from itertools import tee 3 | from sys import maxsize 4 | from collections import defaultdict 5 | 6 | EPS = 1e-4 7 | 8 | 9 | def add_bi_edge(G, src, dest, capacity=None): 10 | G.add_edge(src, dest) 11 | G.add_edge(dest, src) 12 | if capacity: 13 | G[src][dest]["capacity"] = capacity 14 | G[dest][src]["capacity"] = capacity 15 | 16 | 17 | def assert_flow_conservation(flow_list, commod_key): 18 | if len(flow_list) == 0: 19 | return 0.0 20 | 21 | src = commod_key[-1][0] 22 | sink = commod_key[-1][1] 23 | 24 | in_flow, out_flow = defaultdict(float), defaultdict(float) 25 | 26 | for (u, v), l in flow_list: 27 | in_flow[v] += l 28 | out_flow[u] += l 29 | 30 | in_flow = dict(in_flow) 31 | out_flow = dict(out_flow) 32 | 33 | if src in in_flow: 34 | assert False 35 | 36 | if sink in out_flow: 37 | assert False 38 | 39 | assert abs(out_flow[src] - in_flow[sink]) < EPS 40 | assert out_flow[src] > -EPS 41 | 42 | for node in in_flow.keys(): 43 | if node == sink or node == src: 44 | continue 45 | assert abs(in_flow[node] - out_flow[node]) < EPS 46 | 47 | return out_flow[src] 48 | 49 | 50 | # subtract flows in sol_dict from edges in G 51 | def compute_residual_problem(problem, sol_dict): 52 | tm = problem.traffic_matrix.tm 53 | for (k, (s_k, t_k, d_k)), flow_list in sol_dict.items(): 54 | out_flow = compute_in_or_out_flow(flow_list, 0, {s_k}) 55 | assert out_flow >= -EPS 56 | if out_flow < 0: 57 | out_flow = 0 58 | new_d_k = d_k - out_flow 59 | # clamp new demand to 0.0 to avoid floating point errors 60 | if new_d_k < EPS: 61 | new_d_k = 0.0 62 | tm[s_k, t_k] = new_d_k 63 | 64 | for (u, v), l in flow_list: 65 | problem.G[u][v]["capacity"] -= l 66 | # same here; clamp capacity to 0.0 67 | if problem.G[u][v]["capacity"] < 0.0: 68 | problem.G[u][v]["capacity"] = 0.0 69 | 70 | problem._invalidate_commodity_lists() 71 | return problem 72 | 73 | 74 | # subtract flows in sol_dict from edges in G 75 | def compute_residual_graph(G, sol_dict): 76 | for flow_list in sol_dict.values(): 77 | for (u, v), l in flow_list: 78 | G[u][v]["capacity"] -= l 79 | 80 | 81 | # If target_node is not present in the flow sequence, return [], [] 82 | def get_in_and_out_neighbors(flow_list, target_node): 83 | in_neighbors, out_neighbors = set(), set() 84 | for (u, v), l in flow_list: 85 | if u == target_node: 86 | out_neighbors.add(v) 87 | elif v == target_node: 88 | in_neighbors.add(u) 89 | return in_neighbors, out_neighbors 90 | 91 | 92 | # Return a list of nodes that neighbor one of the nodes in the node set, along 93 | # with the flow that neighbor sent 94 | def neighbors_and_flows(flow_list, edge_idx, node_set={}): 95 | n_and_f = [] 96 | for edge, l in flow_list: 97 | if edge[edge_idx] in node_set: 98 | n_and_f.append((edge[1 + edge_idx], l)) 99 | 100 | return n_and_f 101 | 102 | 103 | # Merges multiple flow entries on the same edge; return values has only unique 104 | # edges and total flow 105 | def merge_flows(flow_list): 106 | result = defaultdict(float) 107 | for (u, v), l in flow_list: 108 | result[(u, v)] += l 109 | return [((u, v), l) for (u, v), l in result.items()] 110 | 111 | 112 | # Compute in flow (edge_idx => -1) or out flow (edge_idx => 0) 113 | # to/from a given set of nodes (node_set) for a flow list 114 | def compute_in_or_out_flow(flow_list, edge_idx, node_set={}): 115 | flow = 0.0 116 | for edge, l in flow_list: 117 | if edge[edge_idx] in node_set: 118 | flow += l 119 | elif edge[1 - edge_idx] in node_set: 120 | flow -= l 121 | 122 | return flow 123 | 124 | 125 | # taken from `pairwise` in Itertools Recipes: 126 | # https://docs.python.org/3/library/itertools.html 127 | def path_to_edge_list(path): 128 | "s -> (s0,s1), (s1,s2), (s2, s3), ..." 129 | a, b = tee(path) 130 | next(b, None) 131 | return zip(a, b) 132 | 133 | 134 | # Assumes that the flow sequence has been sorted. If target_node is not present 135 | # in the flow sequence, return 0.0 flow 136 | # TODO: handle sublists 137 | def flow_through_node(flow_seq, target_node): 138 | for i in range(len(flow_seq)): 139 | (u, v), ll = flow_seq[i] 140 | if v == target_node: 141 | assert i < len(flow_seq) 142 | (u_next, v_next), l_next = flow_seq[i + 1] 143 | assert ll == l_next 144 | assert v == u_next 145 | return ll 146 | return 0.0 147 | 148 | 149 | # Compute and return total flow from solution. Solution is a 150 | # dict of {commodity (k, (s_k, t_k, d_k)): flow_seq [((u, v), l),...]} 151 | def total_flow(solution): 152 | 153 | flow_sum = 0.0 154 | for (_, (s_k, _, _)), flow_seq in solution.items(): 155 | for flow_edge in flow_seq: 156 | if isinstance(flow_edge, tuple): 157 | if flow_edge[0][0] == s_k: 158 | flow_sum += flow_edge[-1] 159 | elif isinstance(flow_edge, list): 160 | for e in flow_edge: 161 | if e[0][0] == s_k: 162 | flow_sum += e[-1] 163 | else: 164 | # TODO: throw error 165 | pass 166 | return flow_sum 167 | 168 | 169 | # @param: _flow_seq: [((u, v), l)...] where (u, v) is an edge, 170 | # and l is the flow along that edge 171 | # @param: src: the source of the flow 172 | # 173 | # Returns sorted list [((u, v), l),...] starting from src. If flow diverges at 174 | # any point, we encode it as a sublist (i.e., we would return a list within a 175 | # list) 176 | def sort_flow_seq(_flow_seq, src): 177 | 178 | # Return (flow, sink) 179 | def sort_flow_seq(to_return, flow_seq, curr_node, curr_flow): 180 | 181 | new_edge_flows = [] 182 | inds_to_delete = [] 183 | 184 | for i, edge_flow in enumerate(flow_seq): 185 | (u, v), ll = edge_flow 186 | if curr_node == u: 187 | # Flow should never increase along a path; 188 | # if it does, then it means we must be forking 189 | if ll > curr_flow: 190 | # If the flow increased, that means that we're joining up 191 | # with another edge. We're done traversing down this path 192 | break 193 | new_edge_flows.append(edge_flow) 194 | inds_to_delete.append(i) 195 | 196 | # Delete in descending order; otherwise, we'll get index-out-of-range 197 | # errors 198 | inds_to_delete.reverse() 199 | 200 | if len(new_edge_flows) == 0: 201 | # Base Case: return current node, which must be the sink 202 | return curr_node 203 | 204 | if len(new_edge_flows) == 1: 205 | # The flow is continuing to one other node; append to to_return 206 | # and continue 207 | new_edge_flow = new_edge_flows[0] # (u, v), l 208 | (_, v), ll = new_edge_flow 209 | assert ll == curr_flow or curr_flow == float("inf") 210 | to_return.append(new_edge_flow) 211 | del flow_seq[inds_to_delete[0]] 212 | return sort_flow_seq(to_return, flow_seq, v, ll) 213 | 214 | if len(new_edge_flows) > 1: 215 | # This flow is splitting into multiple sub-flows. 216 | # Append a list for each sub-flow; each list should 217 | # end with the same sink, which may or may not be our 218 | # original sink. The sum of all the sub-flows should 219 | # equal our current flow. 220 | for i in inds_to_delete: 221 | del flow_seq[i] 222 | 223 | flow_sum = 0.0 224 | new_src = None 225 | for new_edge_flow in new_edge_flows: 226 | (_, v), ll = new_edge_flow 227 | new_sub_flow = [new_edge_flow] 228 | sink = sort_flow_seq(new_sub_flow, flow_seq, v, ll) 229 | # All the sub-flows end up at the same sink 230 | assert new_src == sink or new_src is None 231 | new_src = sink 232 | flow_sum += ll 233 | to_return.append(new_sub_flow) 234 | # all the sub-flows add up to the total flow 235 | assert flow_sum == curr_flow or curr_flow == float("inf") 236 | curr_flow = flow_sum 237 | 238 | return sort_flow_seq(to_return, flow_seq, new_src, curr_flow) 239 | 240 | to_return = [] 241 | sort_flow_seq(to_return, _flow_seq.copy(), src, curr_flow=float("inf")) 242 | return to_return 243 | 244 | 245 | # Assumes mat is 2D 246 | def commodity_gen(mat, with_val=True, skip_zero=True): 247 | for x in range(mat.shape[0]): 248 | for y in range(mat.shape[-1]): 249 | # always skip diagonal values 250 | if x == y: 251 | continue 252 | if skip_zero and mat[x, y] == 0: 253 | continue 254 | if with_val: 255 | yield x, y, mat[x, y] 256 | else: 257 | yield x, y 258 | 259 | 260 | def transform_for_network_simplex(problem, vis=False): 261 | 262 | G = problem.G.copy() 263 | node_index = len(G.nodes) 264 | for _, (s_k, t_k, d_k) in problem.commodity_list: 265 | # First add new source and sink nodes to the graph 266 | new_src, new_sink = node_index, node_index + 1 267 | if vis: 268 | src_pos, sink_pos = G.nodes[s_k]["pos"], G.nodes[t_k]["pos"] 269 | new_src_pos = src_pos[0] + uni_rand(-2, 0), \ 270 | src_pos[-1] + uni_rand(-2, 0) 271 | new_sink_pos = sink_pos[0] - uni_rand(-2, 0), \ 272 | sink_pos[-1] - uni_rand(-2, 0) 273 | 274 | G.add_node(new_src, demand=-d_k, label="{}: {}".format(new_src, -d_k)) 275 | G.add_node(new_sink, demand=d_k, label="{}: {}".format(new_sink, d_k)) 276 | if vis: 277 | G[new_src]["pos"] = new_src_pos 278 | G[new_sink]["pos"] = new_sink_pos 279 | 280 | # Then add edge in both directions connecting new source 281 | # to old with infinite capacity 282 | G.add_edge(new_src, s_k, weight=1, capacity=maxsize) 283 | G.add_edge(s_k, new_src, weight=1, capacity=maxsize) 284 | 285 | # Same for sink 286 | G.add_edge(new_sink, t_k, capacity=maxsize) 287 | G.add_edge(t_k, new_sink, capacity=maxsize) 288 | 289 | node_index += 2 290 | 291 | return G 292 | -------------------------------------------------------------------------------- /lib/path_utils.py: -------------------------------------------------------------------------------- 1 | from .graph_utils import path_to_edge_list 2 | from itertools import islice 3 | import networkx as nx 4 | from sys import maxsize 5 | 6 | 7 | # Remove cycles from path 8 | def remove_cycles(path): 9 | stack = [] 10 | visited = set() 11 | for node in path: 12 | if node in visited: 13 | # remove elements from this cycle 14 | while stack[-1] != node: 15 | visited.remove(stack[-1]) 16 | stack = stack[:-1] 17 | else: 18 | stack.append(node) 19 | visited.add(node) 20 | return stack 21 | 22 | 23 | def graph_copy_with_edge_weights(_G, dist_metric): 24 | G = _G.copy() 25 | 26 | if dist_metric == "inv-cap": 27 | for u, v, cap in G.edges.data("capacity"): 28 | if cap < 0.0: 29 | cap = 0.0 30 | try: 31 | G[u][v]["weight"] = 1.0 / cap 32 | except ZeroDivisionError: 33 | G[u][v]["weight"] = maxsize 34 | elif dist_metric == "min-hop": 35 | for u, v, cap in G.edges.data("capacity"): 36 | if cap <= 0.0: 37 | G[u][v]["weight"] = maxsize 38 | else: 39 | G[u][v]["weight"] = 1.0 40 | else: 41 | raise Exception("invalid dist_metric: {}".format(dist_metric)) 42 | 43 | return G 44 | 45 | 46 | def find_paths(G, s_k, t_k, num_paths, disjoint=True, include_weight=False): 47 | def compute_weight(G, path): 48 | return sum(G[u][v]["weight"] for u, v in path_to_edge_list(path)) 49 | 50 | def k_shortest_paths(G, source, target, k, weight="weight"): 51 | try: 52 | # Yen's shortest path algorithm 53 | return list( 54 | islice(nx.shortest_simple_paths( 55 | G, source, target, weight=weight), k) 56 | ) 57 | except nx.NetworkXNoPath: 58 | return [] 59 | 60 | def k_shortest_edge_disjoint_paths(G, source, target, k, weight="weight"): 61 | def compute_distance(path): 62 | return sum(G[u][v][weight] for u, v in path_to_edge_list(path)) 63 | 64 | return [ 65 | remove_cycles(path) 66 | for path in sorted( 67 | nx.edge_disjoint_paths(G, s_k, t_k), 68 | key=lambda path: compute_distance(path), 69 | )[:k] 70 | ] 71 | 72 | if disjoint: 73 | if include_weight: 74 | return [ 75 | (path, compute_weight(path)) 76 | for path in k_shortest_edge_disjoint_paths( 77 | G, s_k, t_k, num_paths, weight="weight" 78 | ) 79 | ] 80 | else: 81 | return k_shortest_edge_disjoint_paths( 82 | G, s_k, t_k, num_paths, weight="weight" 83 | ) 84 | else: 85 | if include_weight: 86 | return [ 87 | (path, compute_weight(path)) 88 | for path in k_shortest_paths( 89 | G, s_k, t_k, num_paths, weight="weight") 90 | ] 91 | else: 92 | return k_shortest_paths(G, s_k, t_k, num_paths, weight="weight") 93 | -------------------------------------------------------------------------------- /lib/teal_actor.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | import os 4 | 5 | import torch 6 | import torch.nn as nn 7 | import torch.optim as optim 8 | import torch.nn.functional as F 9 | from torch.distributions.normal import Normal 10 | from torch.distributions.multinomial import Multinomial 11 | 12 | from .FlowGNN import FlowGNN 13 | from .utils import weight_initialization, print_ 14 | 15 | 16 | class TealActor(nn.Module): 17 | 18 | def __init__( 19 | self, teal_env, num_layer, model_dir, model_save, device, 20 | std=1, log_std_min=-10.0, log_std_max=10.0): 21 | """Initialize teal actor. 22 | 23 | Args: 24 | teal_env: teal environment 25 | num_layer: num of layers in flowGNN 26 | model_dir: model save directory 27 | model_save: whether to save the model 28 | device: device id 29 | std: std value, -1 if apply neuro networks for std 30 | log_std_min: lower bound for log std 31 | log_std_max: upper bound for log std 32 | """ 33 | 34 | super(TealActor, self).__init__() 35 | 36 | # teal environment 37 | self.env = teal_env 38 | self.num_path = self.env.num_path 39 | self.num_path_node = self.env.num_path_node 40 | 41 | # init FlowGNN 42 | self.device = device 43 | self.FlowGNN = FlowGNN(self.env, num_layer).to(self.device) 44 | 45 | # init COMA policy 46 | self.std = std 47 | self.log_std_max = log_std_max 48 | self.log_std_min = log_std_min 49 | self.mean_linear = nn.Linear( 50 | self.num_path*(self.FlowGNN.num_layer+1), 51 | self.num_path).to(self.device) 52 | # apply neuro networks for std 53 | if std < 0: 54 | self.log_std_linear = nn.Linear( 55 | self.num_path*(self.FlowGNN.num_layer+1), 56 | self.num_path).to(self.device) 57 | 58 | # get model fname 59 | self.model_fname = self.model_full_fname( 60 | model_dir, self.env.topo, num_layer, std) 61 | self.model_save = model_save 62 | # load model 63 | self.load_model() 64 | 65 | def model_full_fname(self, model_dir, topo, num_layer, std): 66 | """Return full name of the ML model.""" 67 | 68 | return os.path.join( 69 | model_dir, "{}_flowGNN-{}_std-{}.pt".format( 70 | topo, num_layer, std < 0)) 71 | 72 | def load_model(self): 73 | """Load from model fname.""" 74 | 75 | if os.path.exists(self.model_fname): 76 | print_(f'Loading Teal model from {self.model_fname}') 77 | if self.device.type == 'cpu': 78 | self.load_state_dict( 79 | torch.load( 80 | self.model_fname, map_location=torch.device('cpu'))) 81 | else: 82 | self.load_state_dict(torch.load(self.model_fname)) 83 | else: 84 | print_(f'Creating model {self.model_fname}') 85 | # weight initialization for neural networks 86 | self.apply(weight_initialization) 87 | 88 | def save_model(self): 89 | """Save from model fname.""" 90 | 91 | if self.model_save: 92 | print_(f'Saving Teal model from {self.model_fname}') 93 | torch.save(self.state_dict(), self.model_fname) 94 | 95 | def forward(self, feature): 96 | """Return mean of normal distribution after forward propagation 97 | 98 | Args: 99 | features: input features including capacity and demands 100 | """ 101 | x = self.FlowGNN(feature) 102 | x = x.reshape( 103 | self.num_path_node//self.num_path, 104 | self.num_path*(self.FlowGNN.num_layer+1)) 105 | mean = self.mean_linear(x) 106 | 107 | # to apply neuro networks for std 108 | if self.std < 0: 109 | log_std = self.log_std_linear(x) 110 | log_std_clamped = torch.clamp( 111 | log_std, 112 | min=self.log_std_min, 113 | max=self.log_std_max) 114 | nn_std = torch.exp(log_std_clamped) 115 | return mean, nn_std 116 | # deterministic std 117 | else: 118 | return mean, self.std 119 | 120 | def evaluate(self, obs, deterministic=False): 121 | """Return raw action before softmax split ratio. 122 | 123 | Args: 124 | obs: input features including capacity and demands 125 | deterministic: whether to have deterministic action 126 | """ 127 | 128 | feature = obs.reshape(-1, 1) 129 | mean, std = self.forward(feature) 130 | mean = mean 131 | 132 | # test mode 133 | if deterministic: 134 | distribution = None 135 | raw_action = mean.detach() 136 | log_probability = None 137 | # train mode 138 | else: 139 | # use normal distribution for action 140 | distribution = Normal(mean, std) 141 | sample = distribution.rsample() 142 | raw_action = sample.detach() 143 | log_probability = distribution.log_prob(raw_action).sum(axis=-1) 144 | 145 | return raw_action, log_probability 146 | 147 | def act(self, obs): 148 | """Return split ratio as action with disabled gradient calculation. 149 | 150 | Args: 151 | obs: input observation including capacity and demands 152 | """ 153 | 154 | with torch.no_grad(): 155 | # deterministic action 156 | raw_action, _ = self.evaluate(obs, deterministic=True) 157 | return raw_action 158 | -------------------------------------------------------------------------------- /lib/teal_env.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import json 3 | import os 4 | import math 5 | import time 6 | import random 7 | from itertools import product 8 | 9 | from networkx.readwrite import json_graph 10 | 11 | import torch 12 | import torch_scatter 13 | import torch.nn as nn 14 | from torch.distributions.normal import Normal 15 | from torch.distributions.uniform import Uniform 16 | 17 | from .config import TOPOLOGIES_DIR 18 | from .ADMM import ADMM 19 | from .path_utils import find_paths, graph_copy_with_edge_weights, remove_cycles 20 | 21 | 22 | class TealEnv(object): 23 | 24 | def __init__( 25 | self, obj, topo, problems, 26 | num_path, edge_disjoint, dist_metric, rho, 27 | train_size, val_size, test_size, num_failure, device, 28 | raw_action_min=-10.0, raw_action_max=10.0): 29 | """Initialize Teal environment. 30 | 31 | Args: 32 | obj: objective 33 | topo: topology name 34 | problems: problem list 35 | num_path: number of paths per demand 36 | edge_disjoint: whether edge-disjoint paths 37 | dist_metric: distance metric for shortest paths 38 | rho: hyperparameter for the augumented Lagranian 39 | train size: train start index, stop index 40 | val size: val start index, stop index 41 | test size: test start index, stop index 42 | device: device id 43 | raw_action_min: min value when clamp raw action 44 | raw_action_max: max value when clamp raw action 45 | """ 46 | 47 | self.obj = obj 48 | self.topo = topo 49 | self.problems = problems 50 | self.num_path = num_path 51 | self.edge_disjoint = edge_disjoint 52 | self.dist_metric = dist_metric 53 | 54 | self.train_start, self.train_stop = train_size 55 | self.val_start, self.val_stop = val_size 56 | self.test_start, self.test_stop = test_size 57 | self.num_failure = num_failure 58 | self.device = device 59 | 60 | # init matrices related to topology 61 | self.G = self._read_graph_json(topo) 62 | self.capacity = torch.FloatTensor( 63 | [float(c_e) for u, v, c_e in self.G.edges.data('capacity')]) 64 | self.num_edge_node = len(self.G.edges) 65 | self.num_path_node = self.num_path * self.G.number_of_nodes()\ 66 | * (self.G.number_of_nodes()-1) 67 | self.edge_index, self.edge_index_values, self.p2e = \ 68 | self.get_topo_matrix(topo, num_path, edge_disjoint, dist_metric) 69 | 70 | # init ADMM 71 | self.ADMM = ADMM( 72 | self.p2e, self.num_path, self.num_path_node, 73 | self.num_edge_node, rho, self.device) 74 | 75 | # min/max value when clamp raw action 76 | self.raw_action_min = raw_action_min 77 | self.raw_action_max = raw_action_max 78 | 79 | self.reset('train') 80 | 81 | def reset(self, mode='test'): 82 | """Reset the initial conditions in the beginning.""" 83 | 84 | if mode == 'train': 85 | self.idx_start, self.idx_stop = self.train_start, self.train_stop 86 | elif mode == 'test': 87 | self.idx_start, self.idx_stop = self.test_start, self.test_stop 88 | else: 89 | self.idx_start, self.idx_stop = self.val_start, self.val_stop 90 | self.idx = self.idx_start 91 | self.obs = self._read_obs() 92 | 93 | def get_obs(self): 94 | """Return observation (capacity + traffic matrix).""" 95 | 96 | return self.obs 97 | 98 | def _read_obs(self): 99 | """Return observation (capacity + traffic matrix) from files.""" 100 | 101 | topo, topo_fname, tm_fname = self.problems[self.idx] 102 | with open(tm_fname, 'rb') as f: 103 | tm = pickle.load(f) 104 | # remove demands within nodes 105 | tm = torch.FloatTensor( 106 | [[ele]*self.num_path for i, ele in enumerate(tm.flatten()) 107 | if i % len(tm) != i//len(tm)]).flatten() 108 | obs = torch.concat([self.capacity, tm]).to(self.device) 109 | # simulate link failures in testing 110 | if self.num_failure > 0 and self.idx_start == self.test_start: 111 | idx_failure = torch.tensor( 112 | random.sample(range(self.num_edge_node), 113 | self.num_failure)).to(self.device) 114 | obs[idx_failure] = 0 115 | return obs 116 | 117 | def _next_obs(self): 118 | """Return next observation (capacity + traffic matrix).""" 119 | 120 | self.idx += 1 121 | if self.idx == self.idx_stop: 122 | self.idx = self.idx_start 123 | self.obs = self._read_obs() 124 | return self.obs 125 | 126 | def render(self): 127 | """Return a dictionary for the details of the current problem""" 128 | 129 | topo, topo_fname, tm_fname = self.problems[self.idx] 130 | problem_dict = { 131 | 'problem_name': topo, 132 | 'obj': self.obj, 133 | 'tm_fname': tm_fname.split('/')[-1], 134 | 'num_node': self.G.number_of_nodes(), 135 | 'num_edge': self.G.number_of_edges(), 136 | 'num_path': self.num_path, 137 | 'edge_disjoint': self.edge_disjoint, 138 | 'dist_metric': self.dist_metric, 139 | 'traffic_model': tm_fname.split('/')[-2], 140 | 'traffic_seed': int(tm_fname.split('_')[-3]), 141 | 'scale_factor': float(tm_fname.split('_')[-2]), 142 | 'total_demand': self.obs[ 143 | -self.num_path_node::self.num_path].sum().item(), 144 | } 145 | return problem_dict 146 | 147 | def step(self, raw_action, num_sample=0, num_admm_step=0): 148 | """Return the reward of current action. 149 | 150 | Args: 151 | raw_action: raw action from actor 152 | num_sample: number of samples for reward during training 153 | num_admm_step: number of ADMM steps during testing 154 | """ 155 | 156 | info = {} 157 | if self.idx_start == self.train_start: 158 | reward = self.take_action(raw_action, num_sample) 159 | else: 160 | start_time = time.time() 161 | action = self.transform_raw_action(raw_action) 162 | if self.obj == 'total_flow': 163 | # total flow require no constraint violation 164 | action = self.ADMM.tune_action(self.obs, action, num_admm_step) 165 | action = self.round_action(action) 166 | info['runtime'] = time.time() - start_time 167 | info['sol_mat'] = self.extract_sol_mat(action) 168 | reward = self.get_obj(action) 169 | 170 | # next observation 171 | self._next_obs() 172 | return reward, info 173 | 174 | def get_obj(self, action): 175 | """Return objective.""" 176 | 177 | if self.obj == 'total_flow': 178 | return action.sum(axis=-1) 179 | elif self.obj == 'min_max_link_util': 180 | return (torch_scatter.scatter( 181 | action[self.p2e[0]], self.p2e[1] 182 | )/self.obs[:-self.num_path_node]).max() 183 | 184 | def transform_raw_action(self, raw_action): 185 | """Return network flow allocation as action. 186 | 187 | Args: 188 | raw_action: raw action directly from ML output 189 | """ 190 | # clamp raw action between raw_action_min and raw_action_max 191 | raw_action = torch.clamp( 192 | raw_action, min=self.raw_action_min, max=self.raw_action_max) 193 | 194 | # translate ML output to split ratio through softmax 195 | # 1 in softmax represent unallocated traffic 196 | raw_action = raw_action.exp() 197 | raw_action = raw_action/(1+raw_action.sum(axis=-1)[:, None]) 198 | 199 | # translate split ratio to flow 200 | raw_action = raw_action.flatten() * self.obs[-self.num_path_node:] 201 | 202 | return raw_action 203 | 204 | def round_action( 205 | self, action, round_demand=True, round_capacity=True, 206 | num_round_iter=2): 207 | """Return rounded action. 208 | Action can still violate constraints even after ADMM fine-tuning. 209 | This function rounds the action through cutting flow. 210 | 211 | Args: 212 | action: input action 213 | round_demand: whether to round action for demand constraints 214 | round_capacity: whether to round action for capacity constraints 215 | num_round_iter: number of rounds when iteratively cutting flow 216 | """ 217 | 218 | demand = self.obs[-self.num_path_node::self.num_path] 219 | capacity = self.obs[:-self.num_path_node] 220 | 221 | # reduce action proportionally if action exceed demand 222 | if round_demand: 223 | action = action.reshape(-1, self.num_path) 224 | ratio = action.sum(-1) / demand 225 | action[ratio > 1, :] /= ratio[ratio > 1, None] 226 | action = action.flatten() 227 | 228 | # iteratively reduce action proportionally if action exceed capacity 229 | if round_capacity: 230 | path_flow = action 231 | path_flow_allocated_total = torch.zeros(path_flow.shape)\ 232 | .to(self.device) 233 | for round_iter in range(num_round_iter): 234 | # flow on each edge 235 | edge_flow = torch_scatter.scatter( 236 | path_flow[self.p2e[0]], self.p2e[1]) 237 | # util of each edge 238 | util = 1 + (edge_flow/capacity-1).relu() 239 | # propotionally cut path flow by max util 240 | util = torch_scatter.scatter( 241 | util[self.p2e[1]], self.p2e[0], reduce="max") 242 | path_flow_allocated = path_flow/util 243 | # update total allocation, residual capacity, residual flow 244 | path_flow_allocated_total += path_flow_allocated 245 | if round_iter != num_round_iter - 1: 246 | capacity = (capacity - torch_scatter.scatter( 247 | path_flow_allocated[self.p2e[0]], self.p2e[1])).relu() 248 | path_flow = path_flow - path_flow_allocated 249 | action = path_flow_allocated_total 250 | 251 | return action 252 | 253 | def take_action(self, raw_action, num_sample): 254 | '''Return an approximate reward for action for each node pair. 255 | To make function fast and scalable on GPU, we only calculate delta. 256 | We assume when changing action in one node pair: 257 | (1) The change in edge utilization is very small; 258 | (2) The bottleneck edge in a path does not change due to (1). 259 | For evary path after change: 260 | path_flow/max(util, 1) => 261 | (path_flow+delta_path_flow)/max(util+delta_util, 1) 262 | if util < 1: 263 | reward = - delta_path_flow 264 | if util > 1: 265 | reward = - delta_path_flow/(util+delta_util) 266 | + path_flow*delta_util/(util+delta_util)/util 267 | approx delta_path_flow/util - path_flow/util^2*delta_util 268 | 269 | Args: 270 | raw_action: raw action from policy network 271 | num_sample: number of samples in estimating reward 272 | ''' 273 | 274 | path_flow = self.transform_raw_action(raw_action) 275 | edge_flow = torch_scatter.scatter(path_flow[self.p2e[0]], self.p2e[1]) 276 | util = edge_flow/self.obs[:-self.num_path_node] 277 | 278 | # sample from uniform distribution [mean_min, min_max] 279 | distribution = Uniform( 280 | torch.ones(raw_action.shape).to(self.device)*self.raw_action_min, 281 | torch.ones(raw_action.shape).to(self.device)*self.raw_action_max) 282 | reward = torch.zeros(self.num_path_node//self.num_path).to(self.device) 283 | 284 | if self.obj == 'total_flow': 285 | 286 | # find bottlenack edge for each path 287 | util, path_bottleneck = torch_scatter.scatter_max( 288 | util[self.p2e[1]], self.p2e[0]) 289 | path_bottleneck = self.p2e[1][path_bottleneck] 290 | 291 | # prepare -path_flow/util^2 for reward 292 | coef = path_flow/util**2 293 | coef[util < 1] = 0 294 | coef = torch_scatter.scatter( 295 | coef, path_bottleneck).reshape(-1, 1) 296 | 297 | # prepare path_util to bottleneck edge_util 298 | bottleneck_p2e = torch.sparse_coo_tensor( 299 | self.p2e, (1/self.obs[:-self.num_path_node])[self.p2e[1]], 300 | [self.num_path_node, self.num_edge_node]) 301 | 302 | # sample raw_actions and change each node pair at a time for reward 303 | for _ in range(num_sample): 304 | sample = distribution.rsample() 305 | 306 | # add -delta_path_flow if util < 1 else -delta_path_flow/util 307 | delta_path_flow = self.transform_raw_action(sample) - path_flow 308 | reward += -(delta_path_flow/(1+(util-1).relu()))\ 309 | .reshape(-1, self.num_path).sum(-1) 310 | 311 | # add path_flow/util^2*delta_util for each path 312 | delta_path_flow = torch.sparse_coo_tensor( 313 | torch.stack( 314 | [torch.arange(self.num_path_node//self.num_path) 315 | .to(self.device).repeat_interleave(self.num_path), 316 | torch.arange(self.num_path_node).to(self.device)]), 317 | delta_path_flow, 318 | [self.num_path_node//self.num_path, self.num_path_node]) 319 | # get utilization changes on edge 320 | # do not use torch_sparse.spspmm() 321 | # "an illegal memory access was encountered" in large topology 322 | delta_util = torch.sparse.mm(delta_path_flow, bottleneck_p2e) 323 | reward += torch.sparse.mm(delta_util, coef).flatten() 324 | 325 | elif self.obj == 'min_max_link_util': 326 | 327 | # find link with max utilization 328 | max_util_edge = util.argmax() 329 | 330 | # prepare paths related to max_util_edge 331 | max_util_paths = torch.zeros(self.num_path_node).to(self.device) 332 | max_util_paths[self.p2e[0, self.p2e[1] == max_util_edge]] =\ 333 | 1/self.obs[max_util_edge] 334 | 335 | # sample raw_actions and change each node pair at a time for reward 336 | for _ in range(num_sample): 337 | sample = distribution.rsample() 338 | 339 | delta_path_flow = self.transform_raw_action(sample) - path_flow 340 | delta_path_flow = torch.sparse_coo_tensor( 341 | torch.stack( 342 | [torch.arange(self.num_path_node//self.num_path) 343 | .to(self.device).repeat_interleave(self.num_path), 344 | torch.arange(self.num_path_node).to(self.device)]), 345 | delta_path_flow, 346 | [self.num_path_node//self.num_path, self.num_path_node]) 347 | reward += torch.sparse.mm( 348 | delta_path_flow, max_util_paths.reshape(-1, 1)).flatten() 349 | 350 | return reward/num_sample 351 | 352 | def _read_graph_json(self, topo): 353 | """Return network topo from json file.""" 354 | 355 | assert topo.endswith(".json") 356 | with open(os.path.join(TOPOLOGIES_DIR, topo)) as f: 357 | data = json.load(f) 358 | return json_graph.node_link_graph(data) 359 | 360 | def path_full_fname(self, topo, num_path, edge_disjoint, dist_metric): 361 | """Return full name of the topology path.""" 362 | 363 | return os.path.join( 364 | TOPOLOGIES_DIR, "paths", "path-form", 365 | "{}-{}-paths_edge-disjoint-{}_dist-metric-{}-dict.pkl".format( 366 | topo, num_path, edge_disjoint, dist_metric)) 367 | 368 | def get_path(self, topo, num_path, edge_disjoint, dist_metric): 369 | """Return path dictionary.""" 370 | 371 | self.path_fname = self.path_full_fname( 372 | topo, num_path, edge_disjoint, dist_metric) 373 | print("Loading paths from pickle file", self.path_fname) 374 | try: 375 | with open(self.path_fname, 'rb') as f: 376 | path_dict = pickle.load(f) 377 | print("path_dict size:", len(path_dict)) 378 | return path_dict 379 | except FileNotFoundError: 380 | print("Creating paths {}".format(self.path_fname)) 381 | path_dict = self.compute_path( 382 | topo, num_path, edge_disjoint, dist_metric) 383 | print("Saving paths to pickle file") 384 | with open(self.path_fname, "wb") as w: 385 | pickle.dump(path_dict, w) 386 | return path_dict 387 | 388 | def compute_path(self, topo, num_path, edge_disjoint, dist_metric): 389 | """Return path dictionary through computation.""" 390 | 391 | path_dict = {} 392 | G = graph_copy_with_edge_weights(self.G, dist_metric) 393 | for s_k in G.nodes: 394 | for t_k in G.nodes: 395 | if s_k == t_k: 396 | continue 397 | paths = find_paths(G, s_k, t_k, num_path, edge_disjoint) 398 | paths_no_cycles = [remove_cycles(path) for path in paths] 399 | path_dict[(s_k, t_k)] = paths_no_cycles 400 | return path_dict 401 | 402 | def get_regular_path(self, topo, num_path, edge_disjoint, dist_metric): 403 | """Return path dictionary with the same number of paths per demand. 404 | Fill with the first path when number of paths is not enough. 405 | """ 406 | 407 | path_dict = self.get_path(topo, num_path, edge_disjoint, dist_metric) 408 | for (s_k, t_k) in path_dict: 409 | if len(path_dict[(s_k, t_k)]) < self.num_path: 410 | path_dict[(s_k, t_k)] = [ 411 | path_dict[(s_k, t_k)][0] for _ 412 | in range(self.num_path - len(path_dict[(s_k, t_k)]))]\ 413 | + path_dict[(s_k, t_k)] 414 | elif len(path_dict[(s_k, t_k)]) > self.num_path: 415 | path_dict[(s_k, t_k)] = path_dict[(s_k, t_k)][:self.num_path] 416 | return path_dict 417 | 418 | def get_topo_matrix(self, topo, num_path, edge_disjoint, dist_metric): 419 | """ 420 | Return matrices related to topology. 421 | edge_index, edge_index_values: index and value for matrix 422 | D^(-0.5)*(adjacent)*D^(-0.5) without self-loop 423 | p2e: [path_node_idx, edge_nodes_inx] 424 | """ 425 | 426 | # get regular path dict 427 | path_dict = self.get_regular_path( 428 | topo, num_path, edge_disjoint, dist_metric) 429 | 430 | # edge nodes' degree, index lookup 431 | edge2idx_dict = {edge: idx for idx, edge in enumerate(self.G.edges)} 432 | node2degree_dict = {} 433 | edge_num = len(self.G.edges) 434 | 435 | # build edge_index 436 | src, dst, path_i = [], [], 0 437 | for s in range(len(self.G)): 438 | for t in range(len(self.G)): 439 | if s == t: 440 | continue 441 | for path in path_dict[(s, t)]: 442 | for (u, v) in zip(path[:-1], path[1:]): 443 | src.append(edge_num+path_i) 444 | dst.append(edge2idx_dict[(u, v)]) 445 | 446 | if src[-1] not in node2degree_dict: 447 | node2degree_dict[src[-1]] = 0 448 | node2degree_dict[src[-1]] += 1 449 | if dst[-1] not in node2degree_dict: 450 | node2degree_dict[dst[-1]] = 0 451 | node2degree_dict[dst[-1]] += 1 452 | path_i += 1 453 | 454 | # edge_index is D^(-0.5)*(adj)*D^(-0.5) without self-loop 455 | edge_index_values = torch.tensor( 456 | [1/math.sqrt(node2degree_dict[u]*node2degree_dict[v]) 457 | for u, v in zip(src+dst, dst+src)]).to(self.device) 458 | edge_index = torch.tensor( 459 | [src+dst, dst+src], dtype=torch.long).to(self.device) 460 | p2e = torch.tensor([src, dst], dtype=torch.long).to(self.device) 461 | p2e[0] -= len(self.G.edges) 462 | 463 | return edge_index, edge_index_values, p2e 464 | 465 | def extract_sol_mat(self, action): 466 | """return sparse solution matrix. 467 | Solution matrix is of dimension num_of_demand x num_of_edge. 468 | The i, j entry represents the traffic flow from demand i on edge j. 469 | """ 470 | 471 | # 3D sparse matrix to represent which path, which demand, which edge 472 | sol_mat_index = torch.stack([ 473 | self.p2e[0] % self.num_path, 474 | torch.div(self.p2e[0], self.num_path, rounding_mode='floor'), 475 | self.p2e[1]]) 476 | 477 | # merge allocation from different paths of the same demand 478 | sol_mat = torch.sparse_coo_tensor( 479 | sol_mat_index, 480 | action[self.p2e[0]], 481 | (self.num_path, 482 | self.num_path_node//self.num_path, 483 | self.num_edge_node)) 484 | sol_mat = torch.sparse.sum(sol_mat, [0]) 485 | 486 | return sol_mat 487 | -------------------------------------------------------------------------------- /lib/teal_model.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import time 3 | import json 4 | import sys 5 | import os 6 | from tqdm import tqdm 7 | from networkx.readwrite import json_graph 8 | 9 | import torch 10 | import torch.nn as nn 11 | import torch.optim as optim 12 | from torch.utils.data import DataLoader 13 | 14 | from .teal_actor import TealActor 15 | from .teal_env import TealEnv 16 | from .utils import print_ 17 | 18 | 19 | class Teal(): 20 | def __init__(self, teal_env, teal_actor, lr, early_stop): 21 | """Initialize Teal model. 22 | 23 | Args: 24 | teal_env: teal environment 25 | num_layer: number of flowGNN layers 26 | lr: learning rate 27 | early_stop: whether to early stop 28 | """ 29 | 30 | self.env = teal_env 31 | self.actor = teal_actor 32 | 33 | # init optimizer 34 | self.actor_optimizer = optim.Adam(self.actor.parameters(), lr=lr) 35 | 36 | # early stop when val result no longer changes 37 | self.early_stop = early_stop 38 | if self.early_stop: 39 | self.val_reward = [] 40 | 41 | def train(self, num_epoch, batch_size, num_sample): 42 | """Train Teal model. 43 | 44 | Args: 45 | num_epoch: number of training epoch 46 | batch_size: batch size 47 | num_sample: number of samples in COMA reward 48 | """ 49 | 50 | for epoch in range(num_epoch): 51 | 52 | self.env.reset('train') 53 | 54 | ids = range(self.env.idx_start, self.env.idx_stop) 55 | loop_obj = tqdm( 56 | [ids[i:i+batch_size] for i in range(0, len(ids), batch_size)], 57 | desc=f"Training epoch {epoch}/{num_epoch}: ") 58 | 59 | for idx in loop_obj: 60 | loss = 0 61 | for _ in idx: 62 | torch.cuda.empty_cache() 63 | 64 | # get observation 65 | obs = self.env.get_obs() 66 | # get action 67 | raw_action, log_probability = self.actor.evaluate(obs) 68 | # get reward 69 | reward, info = self.env.step( 70 | raw_action, num_sample=num_sample) 71 | loss += -(log_probability*reward).mean() 72 | 73 | self.actor_optimizer.zero_grad() 74 | loss.backward() 75 | self.actor_optimizer.step() 76 | # break 77 | 78 | # early stop 79 | if self.early_stop: 80 | self.val() 81 | if len(self.val_reward) > 20 and abs( 82 | sum(self.val_reward[-20:-10])/10 83 | - sum(self.val_reward[-10:])/10) < 0.0001: 84 | break 85 | self.actor.save_model() 86 | 87 | def val(self): 88 | """Validating Teal model.""" 89 | 90 | self.actor.eval() 91 | self.env.reset('val') 92 | 93 | rewards = 0 94 | for idx in range(self.env.idx_start, self.env.idx_stop): 95 | 96 | # get observation 97 | problem_dict = self.env.render() 98 | obs = self.env.get_obs() 99 | # get action 100 | raw_action = self.actor.act(obs) 101 | # get reward 102 | reward, info = self.env.step(raw_action) 103 | # show satisfied demand instead of total flow 104 | rewards += reward.item()/problem_dict['total_demand']\ 105 | if self.env.obj == 'total_flow' else reward.item() 106 | self.val_reward.append( 107 | rewards/(self.env.idx_stop - self.env.idx_start)) 108 | 109 | def test(self, num_admm_step, output_header, output_csv, output_dir): 110 | """Test Teal model. 111 | 112 | Args: 113 | num_admm_step: number of ADMM steps 114 | output_header: header of the output csv 115 | output_csv: name of the output csv 116 | output_dir: directory to save output solution 117 | """ 118 | 119 | self.actor.eval() 120 | self.env.reset('test') 121 | 122 | with open(output_csv, "a") as results: 123 | print_(",".join(output_header), file=results) 124 | 125 | runtime_list, obj_list = [], [] 126 | loop_obj = tqdm( 127 | range(self.env.idx_start, self.env.idx_stop), 128 | desc="Testing: ") 129 | 130 | for idx in loop_obj: 131 | 132 | # get observation 133 | problem_dict = self.env.render() 134 | obs = self.env.get_obs() 135 | # get action 136 | start_time = time.time() 137 | raw_action = self.actor.act(obs) 138 | runtime = time.time() - start_time 139 | # get reward 140 | reward, info = self.env.step( 141 | raw_action, num_admm_step=num_admm_step) 142 | # add runtime in transforming, ADMM, rounding 143 | runtime += info['runtime'] 144 | runtime_list.append(runtime) 145 | # show satisfied demand instead of total flow 146 | obj_list.append( 147 | reward.item()/problem_dict['total_demand'] 148 | if self.env.obj == 'total_flow' else reward.item()) 149 | 150 | # display avg runtime, obj 151 | loop_obj.set_postfix({ 152 | 'runtime': '%.4f' % (sum(runtime_list)/len(runtime_list)), 153 | 'obj': '%.4f' % (sum(obj_list)/len(obj_list)), 154 | }) 155 | 156 | # save solution matrix 157 | sol_mat = info['sol_mat'] 158 | torch.save(sol_mat, os.path.join( 159 | output_dir, 160 | "{}-{}-{}-teal_objective-{}_{}-paths_" 161 | "edge-disjoint-{}_dist-metric-{}_sol-mat.pt".format( 162 | problem_dict['problem_name'], 163 | problem_dict['traffic_model'], 164 | problem_dict['traffic_seed'], 165 | problem_dict['obj'], 166 | problem_dict['num_path'], 167 | problem_dict['edge_disjoint'], 168 | problem_dict['dist_metric']))) 169 | 170 | PLACEHOLDER = ",".join("{}" for _ in output_header) 171 | result_line = PLACEHOLDER.format( 172 | problem_dict['problem_name'], 173 | problem_dict['num_node'], 174 | problem_dict['num_edge'], 175 | problem_dict['traffic_seed'], 176 | problem_dict['scale_factor'], 177 | problem_dict['traffic_model'], 178 | problem_dict['total_demand'], 179 | "Teal", 180 | problem_dict['num_path'], 181 | problem_dict['edge_disjoint'], 182 | problem_dict['dist_metric'], 183 | problem_dict['obj'], 184 | reward, 185 | runtime) 186 | print_(result_line, file=results) 187 | # break 188 | -------------------------------------------------------------------------------- /lib/utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from contextlib import contextmanager 3 | 4 | import torch 5 | import torch.nn as nn 6 | 7 | 8 | def weight_initialization(module): 9 | """Initialize weights in nn module""" 10 | 11 | if isinstance(module, nn.Linear): 12 | torch.nn.init.xavier_uniform_(module.weight, gain=1) 13 | torch.nn.init.constant_(module.bias, 0) 14 | 15 | 16 | def uni_rand(low=-1, high=1): 17 | """Uniform random variable [low, high)""" 18 | return (high - low) * np.random.rand() + low 19 | 20 | 21 | def print_(*args, file=None): 22 | """print out *args to file""" 23 | if file is None: 24 | file = sys.stdout 25 | print(*args, file=file) 26 | file.flush() 27 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | kmodes==0.11.0 2 | pathos==0.2.7 3 | tqdm==4.64.0 4 | -------------------------------------------------------------------------------- /run/teal.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | from teal_helper import get_args_and_problems, print_, PATH_FORM_HYPERPARAMS 4 | 5 | import os 6 | import sys 7 | 8 | import torch 9 | 10 | sys.path.append('..') 11 | 12 | from lib.teal_env import TealEnv 13 | from lib.teal_actor import TealActor 14 | from lib.teal_model import Teal 15 | 16 | 17 | TOP_DIR = "teal-logs" 18 | MODEL_DIR = "teal-models" 19 | HEADERS = [ 20 | "problem", 21 | "num_nodes", 22 | "num_edges", 23 | "traffic_seed", 24 | "scale_factor", 25 | "tm_model", 26 | "total_demand", 27 | "algo", 28 | "num_paths", 29 | "edge_disjoint", 30 | "dist_metric", 31 | "objective", 32 | "obj_val", 33 | "runtime", 34 | ] 35 | 36 | OUTPUT_CSV_TEMPLATE = "teal-{}-{}.csv" 37 | 38 | 39 | def benchmark(problems, output_csv, arg): 40 | 41 | num_path, edge_disjoint, dist_metric = PATH_FORM_HYPERPARAMS 42 | obj, topo = args.obj, args.topo 43 | model_save = args.model_save 44 | device = torch.device( 45 | f"cuda:{args.devid}" if torch.cuda.is_available() else "cpu") 46 | 47 | # ========== load hyperparameters 48 | # env hyper-parameters 49 | train_size = [args.slice_train_start, args.slice_train_stop] 50 | val_size = [args.slice_val_start, args.slice_val_stop] 51 | test_size = [args.slice_test_start, args.slice_test_stop] 52 | # actor hyper-parameters 53 | num_layer = args.layers 54 | rho = args.rho 55 | # training hyper-parameters 56 | lr = args.lr 57 | early_stop = args.early_stop 58 | num_epoch = args.epochs 59 | batch_size = args.bsz 60 | num_sample = args.samples 61 | num_admm_step = args.admm_steps 62 | # testing hyper-parameters 63 | num_failure = args.failures 64 | 65 | # ========== init teal env, actor, model 66 | teal_env = TealEnv( 67 | obj=obj, 68 | topo=topo, 69 | problems=problems, 70 | num_path=num_path, 71 | edge_disjoint=edge_disjoint, 72 | dist_metric=dist_metric, 73 | rho=rho, 74 | train_size=train_size, 75 | val_size=val_size, 76 | test_size=test_size, 77 | num_failure=num_failure, 78 | device=device) 79 | teal_actor = TealActor( 80 | teal_env=teal_env, 81 | num_layer=num_layer, 82 | model_dir=MODEL_DIR, 83 | model_save=model_save, 84 | device=device) 85 | teal = Teal( 86 | teal_env=teal_env, 87 | teal_actor=teal_actor, 88 | lr=lr, 89 | early_stop=early_stop) 90 | 91 | # ========== train and test 92 | teal.train( 93 | num_epoch=num_epoch, 94 | batch_size=batch_size, 95 | num_sample=num_sample) 96 | teal.test( 97 | num_admm_step=num_admm_step, 98 | output_header=HEADERS, 99 | output_csv=output_csv, 100 | output_dir=TOP_DIR) 101 | 102 | return 103 | 104 | 105 | if __name__ == '__main__': 106 | 107 | if not os.path.exists(TOP_DIR): 108 | os.makedirs(TOP_DIR) 109 | if not os.path.exists(MODEL_DIR): 110 | os.makedirs(MODEL_DIR) 111 | 112 | args, output_csv, problems = get_args_and_problems(OUTPUT_CSV_TEMPLATE) 113 | 114 | if args.dry_run: 115 | print("Problems to run:") 116 | for problem in problems: 117 | print(problem) 118 | else: 119 | benchmark(problems, output_csv, args) 120 | -------------------------------------------------------------------------------- /run/teal_helper.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from glob import iglob 3 | 4 | import argparse 5 | import os 6 | import sys 7 | 8 | sys.path.append("..") 9 | 10 | from lib.config import TOPOLOGIES_DIR, TM_DIR 11 | 12 | PROBLEM_NAMES = [ 13 | 'B4.json', 14 | 'UsCarrier.json', 15 | 'Kdl.json', 16 | 'ASN2k.json', 17 | ] 18 | TM_MODELS = [ 19 | "real", 20 | "toy", 21 | ] 22 | SCALE_FACTORS = [1.0] 23 | OBJ_STRS = ["total_flow", "min_max_link_util"] 24 | 25 | PATH_FORM_HYPERPARAMS = (4, True, "min-hop") 26 | 27 | PROBLEM_NAMES_AND_TM_MODELS = [ 28 | (prob_name, tm_model) for prob_name in PROBLEM_NAMES 29 | for tm_model in TM_MODELS 30 | ] 31 | 32 | PROBLEMS = [] 33 | GROUPED_BY_PROBLEMS = defaultdict(list) 34 | HOLDOUT_PROBLEMS = [] 35 | GROUPED_BY_HOLDOUT_PROBLEMS = defaultdict(list) 36 | 37 | for problem_name in PROBLEM_NAMES: 38 | if problem_name.endswith(".graphml"): 39 | topo_fname = os.path.join(TOPOLOGIES_DIR, "topology-zoo", problem_name) 40 | else: 41 | topo_fname = os.path.join(TOPOLOGIES_DIR, problem_name) 42 | for model in TM_MODELS: 43 | for tm_fname in iglob( 44 | "{}/{}/{}*_traffic-matrix.pkl".format(TM_DIR, model, problem_name) 45 | ): 46 | vals = os.path.basename(tm_fname)[:-4].split("_") 47 | _, traffic_seed, scale_factor = vals[1], int(vals[2]),\ 48 | float(vals[3]) 49 | GROUPED_BY_PROBLEMS[(problem_name, model, scale_factor)].append( 50 | (topo_fname, tm_fname) 51 | ) 52 | PROBLEMS.append((problem_name, topo_fname, tm_fname)) 53 | for tm_fname in iglob( 54 | "{}/holdout/{}/{}*_traffic-matrix.pkl".format( 55 | TM_DIR, model, problem_name 56 | ) 57 | ): 58 | vals = os.path.basename(tm_fname)[:-4].split("_") 59 | _, traffic_seed, scale_factor = vals[1], int(vals[2]),\ 60 | float(vals[3]) 61 | GROUPED_BY_HOLDOUT_PROBLEMS[(problem_name, model, scale_factor)]\ 62 | .append( 63 | (topo_fname, tm_fname) 64 | ) 65 | HOLDOUT_PROBLEMS.append((problem_name, topo_fname, tm_fname)) 66 | 67 | GROUPED_BY_PROBLEMS = dict(GROUPED_BY_PROBLEMS) 68 | for key, vals in GROUPED_BY_PROBLEMS.items(): 69 | GROUPED_BY_PROBLEMS[key] = sorted( 70 | vals, key=lambda x: int(x[-1].split('_')[-3])) 71 | 72 | GROUPED_BY_HOLDOUT_PROBLEMS = dict(GROUPED_BY_HOLDOUT_PROBLEMS) 73 | for key, vals in GROUPED_BY_HOLDOUT_PROBLEMS.items(): 74 | GROUPED_BY_HOLDOUT_PROBLEMS[key] = sorted( 75 | vals, key=lambda x: int(x[-1].split('_')[-3])) 76 | 77 | 78 | def get_problems(args): 79 | if (args.topo, args.tm_model, args.scale_factor) not in GROUPED_BY_PROBLEMS: 80 | raise Exception('Traffic matrices not found') 81 | problems = [] 82 | for topo_fname, tm_fname in GROUPED_BY_PROBLEMS[ 83 | (args.topo, args.tm_model, args.scale_factor)]: 84 | problems.append((args.topo, topo_fname, tm_fname)) 85 | return problems 86 | 87 | 88 | def get_args_and_problems(formatted_fname_template, additional_args=[]): 89 | parser = argparse.ArgumentParser() 90 | 91 | # Problems arguments 92 | parser.add_argument( 93 | "--dry-run", dest="dry_run", default=False, action="store_true", 94 | help="list problems to run") 95 | parser.add_argument( 96 | "--obj", type=str, default='total_flow', choices=OBJ_STRS, 97 | help="objective function") 98 | parser.add_argument( 99 | "--tm-model", type=str, default='real', choices=TM_MODELS, 100 | help="traffic matrix model") 101 | parser.add_argument( 102 | "--topo", type=str, required=True, choices=PROBLEM_NAMES, 103 | help="network topology") 104 | parser.add_argument( 105 | "--scale-factor", type=float, default=1.0, choices=SCALE_FACTORS, 106 | help="traffic matrix scale factor") 107 | parser.add_argument( 108 | '--devid', type=int, default=0, 109 | help='GPU device id') 110 | parser.add_argument( 111 | '--model-save', type=bool, default=False, 112 | help='whether to save model') 113 | 114 | # env hyper-parameters 115 | parser.add_argument( 116 | '--slice-train-start', type=int, default=0, 117 | help="start index of training") 118 | parser.add_argument( 119 | '--slice-train-stop', type=int, default=20, 120 | help="end index of training") 121 | parser.add_argument( 122 | '--slice-val-start', type=int, default=20, 123 | help="start index of validation") 124 | parser.add_argument( 125 | '--slice-val-stop', type=int, default=28, 126 | help="end index of validation") 127 | parser.add_argument( 128 | '--slice-test-start', type=int, default=28, 129 | help="start index of testing") 130 | parser.add_argument( 131 | '--slice-test-stop', type=int, default=36, 132 | help="end index of testing") 133 | 134 | # actor hyper-parameters 135 | parser.add_argument( 136 | '--layers', type=int, default=6, 137 | help='number of flowGNN layers') 138 | parser.add_argument( 139 | '--rho', type=float, default=1.0, 140 | help='rho in ADMM') 141 | 142 | # training hyper-parameters 143 | parser.add_argument( 144 | '--lr', type=float, default=0.0001, 145 | help='learning rate') 146 | parser.add_argument( 147 | '--epochs', type=int, default=0, 148 | help='number of training epochs') 149 | parser.add_argument( 150 | '--bsz', type=int, default=20, 151 | help='batch size') 152 | parser.add_argument( 153 | '--samples', type=int, default=5, 154 | help='number of COMA samples') 155 | parser.add_argument( 156 | '--admm-steps', type=int, default=5, 157 | help='number of ADMM steps') 158 | parser.add_argument( 159 | '--early-stop', type=bool, default=False, 160 | help='whether to stop early') 161 | 162 | # testing hyper-parameters 163 | parser.add_argument( 164 | '--failures', type=int, default=0, help='number of edge failures') 165 | 166 | for add_arg in additional_args: 167 | name_or_flags, kwargs = add_arg[0], add_arg[1] 168 | parser.add_argument(name_or_flags, **kwargs) 169 | args = parser.parse_args() 170 | 171 | slice_str = "all" # "slice_" + "_".join(str(i) for i in args.slices) 172 | formatted_fname_substr = formatted_fname_template.format( 173 | args.obj, slice_str) 174 | return args, formatted_fname_substr, get_problems(args) 175 | 176 | 177 | def print_(*args, file=None): 178 | if file is None: 179 | file = sys.stdout 180 | print(*args, file=file) 181 | file.flush() 182 | -------------------------------------------------------------------------------- /topologies/B4.json: -------------------------------------------------------------------------------- 1 | {"directed": true, "multigraph": false, "graph": {}, "nodes": [{"id": 0}, {"id": 1}, {"id": 2}, {"id": 4}, {"id": 3}, {"id": 5}, {"id": 6}, {"id": 7}, {"id": 10}, {"id": 9}, {"id": 8}, {"id": 11}], "links": [{"capacity": 5000.0, "source": 0, "target": 1}, {"capacity": 5000.0, "source": 0, "target": 2}, {"capacity": 5000.0, "source": 1, "target": 0}, {"capacity": 5000.0, "source": 1, "target": 4}, {"capacity": 5000.0, "source": 2, "target": 0}, {"capacity": 5000.0, "source": 2, "target": 3}, {"capacity": 5000.0, "source": 2, "target": 5}, {"capacity": 5000.0, "source": 4, "target": 1}, {"capacity": 5000.0, "source": 4, "target": 3}, {"capacity": 5000.0, "source": 4, "target": 5}, {"capacity": 5000.0, "source": 3, "target": 2}, {"capacity": 5000.0, "source": 3, "target": 4}, {"capacity": 5000.0, "source": 3, "target": 6}, {"capacity": 5000.0, "source": 3, "target": 7}, {"capacity": 5000.0, "source": 5, "target": 2}, {"capacity": 5000.0, "source": 5, "target": 4}, {"capacity": 5000.0, "source": 5, "target": 6}, {"capacity": 5000.0, "source": 5, "target": 7}, {"capacity": 5000.0, "source": 6, "target": 3}, {"capacity": 5000.0, "source": 6, "target": 5}, {"capacity": 5000.0, "source": 6, "target": 7}, {"capacity": 5000.0, "source": 6, "target": 10}, {"capacity": 5000.0, "source": 7, "target": 3}, {"capacity": 5000.0, "source": 7, "target": 5}, {"capacity": 5000.0, "source": 7, "target": 6}, {"capacity": 5000.0, "source": 7, "target": 9}, {"capacity": 5000.0, "source": 10, "target": 6}, {"capacity": 5000.0, "source": 10, "target": 8}, {"capacity": 5000.0, "source": 10, "target": 9}, {"capacity": 5000.0, "source": 10, "target": 11}, {"capacity": 5000.0, "source": 9, "target": 7}, {"capacity": 5000.0, "source": 9, "target": 8}, {"capacity": 5000.0, "source": 9, "target": 10}, {"capacity": 5000.0, "source": 9, "target": 11}, {"capacity": 5000.0, "source": 8, "target": 9}, {"capacity": 5000.0, "source": 8, "target": 10}, {"capacity": 5000.0, "source": 11, "target": 9}, {"capacity": 5000.0, "source": 11, "target": 10}]} -------------------------------------------------------------------------------- /topologies/UsCarrier.json: -------------------------------------------------------------------------------- 1 | {"directed": true, "multigraph": false, "graph": {}, "nodes": [{"id": 0}, {"id": 85}, {"id": 1}, {"id": 7}, {"id": 103}, {"id": 8}, {"id": 9}, {"id": 109}, {"id": 119}, {"id": 81}, {"id": 102}, {"id": 133}, {"id": 21}, {"id": 86}, {"id": 82}, {"id": 106}, {"id": 24}, {"id": 118}, {"id": 80}, {"id": 105}, {"id": 132}, {"id": 138}, {"id": 20}, {"id": 104}, {"id": 67}, {"id": 107}, {"id": 108}, {"id": 26}, {"id": 27}, {"id": 100}, {"id": 116}, {"id": 2}, {"id": 139}, {"id": 77}, {"id": 18}, {"id": 19}, {"id": 101}, {"id": 97}, {"id": 35}, {"id": 117}, {"id": 3}, {"id": 140}, {"id": 68}, {"id": 69}, {"id": 135}, {"id": 151}, {"id": 152}, {"id": 10}, {"id": 11}, {"id": 15}, {"id": 25}, {"id": 91}, {"id": 98}, {"id": 32}, {"id": 114}, {"id": 4}, {"id": 5}, {"id": 141}, {"id": 76}, {"id": 70}, {"id": 49}, {"id": 136}, {"id": 150}, {"id": 13}, {"id": 16}, {"id": 14}, {"id": 66}, {"id": 90}, {"id": 33}, {"id": 48}, {"id": 6}, {"id": 134}, {"id": 72}, {"id": 92}, {"id": 71}, {"id": 157}, {"id": 61}, {"id": 154}, {"id": 12}, {"id": 17}, {"id": 34}, {"id": 36}, {"id": 39}, {"id": 47}, {"id": 137}, {"id": 73}, {"id": 74}, {"id": 75}, {"id": 93}, {"id": 142}, {"id": 156}, {"id": 155}, {"id": 153}, {"id": 30}, {"id": 31}, {"id": 84}, {"id": 37}, {"id": 38}, {"id": 46}, {"id": 111}, {"id": 94}, {"id": 143}, {"id": 60}, {"id": 131}, {"id": 83}, {"id": 41}, {"id": 45}, {"id": 44}, {"id": 87}, {"id": 42}, {"id": 43}, {"id": 40}, {"id": 110}, {"id": 88}, {"id": 95}, {"id": 113}, {"id": 89}, {"id": 124}, {"id": 130}, {"id": 53}, {"id": 50}, {"id": 122}, {"id": 125}, {"id": 52}, {"id": 58}, {"id": 54}, {"id": 112}, {"id": 115}, {"id": 57}, {"id": 51}, {"id": 129}, {"id": 56}, {"id": 128}, {"id": 123}, {"id": 149}, {"id": 127}, {"id": 59}, {"id": 148}, {"id": 78}, {"id": 62}, {"id": 120}, {"id": 79}, {"id": 96}, {"id": 28}, {"id": 55}, {"id": 63}, {"id": 99}, {"id": 126}, {"id": 22}, {"id": 64}, {"id": 121}, {"id": 65}, {"id": 29}, {"id": 23}, {"id": 144}, {"id": 145}, {"id": 146}, {"id": 147}], "links": [{"capacity": 200.0, "source": 0, "target": 85}, {"capacity": 150.0, "source": 85, "target": 0}, {"capacity": 700.0, "source": 85, "target": 1}, {"capacity": 1100.0, "source": 85, "target": 7}, {"capacity": 850.0, "source": 1, "target": 85}, {"capacity": 600.0, "source": 1, "target": 103}, {"capacity": 250.0, "source": 7, "target": 8}, {"capacity": 4900.0, "source": 7, "target": 9}, {"capacity": 4500.0, "source": 7, "target": 109}, {"capacity": 1100.0, "source": 7, "target": 85}, {"capacity": 3500.0, "source": 7, "target": 119}, {"capacity": 750.0, "source": 103, "target": 1}, {"capacity": 200.0, "source": 103, "target": 81}, {"capacity": 350.0, "source": 103, "target": 102}, {"capacity": 750.0, "source": 103, "target": 133}, {"capacity": 500.0, "source": 8, "target": 7}, {"capacity": 250.0, "source": 8, "target": 9}, {"capacity": 5850.0, "source": 9, "target": 7}, {"capacity": 450.0, "source": 9, "target": 8}, {"capacity": 4250.0, "source": 9, "target": 21}, {"capacity": 1450.0, "source": 9, "target": 86}, {"capacity": 4100.0, "source": 109, "target": 7}, {"capacity": 250.0, "source": 109, "target": 82}, {"capacity": 4450.0, "source": 109, "target": 106}, {"capacity": 2650.0, "source": 119, "target": 7}, {"capacity": 1050.0, "source": 119, "target": 24}, {"capacity": 2650.0, "source": 119, "target": 118}, {"capacity": 50.0, "source": 81, "target": 80}, {"capacity": 100.0, "source": 81, "target": 103}, {"capacity": 350.0, "source": 102, "target": 105}, {"capacity": 500.0, "source": 102, "target": 103}, {"capacity": 1100.0, "source": 133, "target": 86}, {"capacity": 900.0, "source": 133, "target": 103}, {"capacity": 1100.0, "source": 133, "target": 132}, {"capacity": 550.0, "source": 133, "target": 138}, {"capacity": 5600.0, "source": 21, "target": 9}, {"capacity": 4300.0, "source": 21, "target": 20}, {"capacity": 1350.0, "source": 86, "target": 9}, {"capacity": 200.0, "source": 86, "target": 80}, {"capacity": 1250.0, "source": 86, "target": 133}, {"capacity": 350.0, "source": 82, "target": 104}, {"capacity": 400.0, "source": 82, "target": 105}, {"capacity": 250.0, "source": 82, "target": 109}, {"capacity": 3950.0, "source": 106, "target": 67}, {"capacity": 350.0, "source": 106, "target": 107}, {"capacity": 600.0, "source": 106, "target": 108}, {"capacity": 4000.0, "source": 106, "target": 109}, {"capacity": 750.0, "source": 24, "target": 26}, {"capacity": 200.0, "source": 24, "target": 27}, {"capacity": 900.0, "source": 24, "target": 119}, {"capacity": 500.0, "source": 118, "target": 100}, {"capacity": 2750.0, "source": 118, "target": 116}, {"capacity": 2000.0, "source": 118, "target": 119}, {"capacity": 100.0, "source": 80, "target": 81}, {"capacity": 100.0, "source": 80, "target": 86}, {"capacity": 400.0, "source": 105, "target": 82}, {"capacity": 400.0, "source": 105, "target": 102}, {"capacity": 1050.0, "source": 132, "target": 2}, {"capacity": 1050.0, "source": 132, "target": 133}, {"capacity": 650.0, "source": 138, "target": 133}, {"capacity": 400.0, "source": 138, "target": 139}, {"capacity": 5450.0, "source": 20, "target": 21}, {"capacity": 4300.0, "source": 20, "target": 77}, {"capacity": 350.0, "source": 104, "target": 82}, {"capacity": 350.0, "source": 104, "target": 107}, {"capacity": 3900.0, "source": 67, "target": 18}, {"capacity": 3650.0, "source": 67, "target": 106}, {"capacity": 500.0, "source": 107, "target": 19}, {"capacity": 300.0, "source": 107, "target": 104}, {"capacity": 300.0, "source": 107, "target": 106}, {"capacity": 400.0, "source": 108, "target": 101}, {"capacity": 600.0, "source": 108, "target": 106}, {"capacity": 700.0, "source": 26, "target": 24}, {"capacity": 750.0, "source": 26, "target": 97}, {"capacity": 100.0, "source": 27, "target": 24}, {"capacity": 450.0, "source": 100, "target": 101}, {"capacity": 450.0, "source": 100, "target": 118}, {"capacity": 2950.0, "source": 116, "target": 35}, {"capacity": 650.0, "source": 116, "target": 117}, {"capacity": 2200.0, "source": 116, "target": 118}, {"capacity": 1050.0, "source": 2, "target": 3}, {"capacity": 1000.0, "source": 2, "target": 132}, {"capacity": 450.0, "source": 139, "target": 138}, {"capacity": 350.0, "source": 139, "target": 140}, {"capacity": 5450.0, "source": 77, "target": 20}, {"capacity": 2400.0, "source": 77, "target": 68}, {"capacity": 300.0, "source": 77, "target": 69}, {"capacity": 2300.0, "source": 77, "target": 135}, {"capacity": 800.0, "source": 77, "target": 151}, {"capacity": 100.0, "source": 77, "target": 152}, {"capacity": 3750.0, "source": 18, "target": 10}, {"capacity": 300.0, "source": 18, "target": 11}, {"capacity": 450.0, "source": 18, "target": 15}, {"capacity": 450.0, "source": 18, "target": 19}, {"capacity": 3650.0, "source": 18, "target": 67}, {"capacity": 450.0, "source": 19, "target": 18}, {"capacity": 450.0, "source": 19, "target": 107}, {"capacity": 400.0, "source": 101, "target": 100}, {"capacity": 550.0, "source": 101, "target": 108}, {"capacity": 100.0, "source": 97, "target": 25}, {"capacity": 600.0, "source": 97, "target": 26}, {"capacity": 600.0, "source": 97, "target": 91}, {"capacity": 100.0, "source": 97, "target": 98}, {"capacity": 2900.0, "source": 35, "target": 32}, {"capacity": 2350.0, "source": 35, "target": 116}, {"capacity": 550.0, "source": 117, "target": 91}, {"capacity": 50.0, "source": 117, "target": 114}, {"capacity": 800.0, "source": 117, "target": 116}, {"capacity": 900.0, "source": 3, "target": 2}, {"capacity": 550.0, "source": 3, "target": 4}, {"capacity": 550.0, "source": 3, "target": 5}, {"capacity": 500.0, "source": 140, "target": 139}, {"capacity": 400.0, "source": 140, "target": 141}, {"capacity": 2300.0, "source": 68, "target": 76}, {"capacity": 3050.0, "source": 68, "target": 77}, {"capacity": 600.0, "source": 69, "target": 77}, {"capacity": 200.0, "source": 69, "target": 70}, {"capacity": 1200.0, "source": 135, "target": 49}, {"capacity": 2250.0, "source": 135, "target": 77}, {"capacity": 1200.0, "source": 135, "target": 136}, {"capacity": 1000.0, "source": 151, "target": 77}, {"capacity": 750.0, "source": 151, "target": 150}, {"capacity": 150.0, "source": 152, "target": 77}, {"capacity": 50.0, "source": 152, "target": 150}, {"capacity": 3450.0, "source": 10, "target": 18}, {"capacity": 200.0, "source": 10, "target": 11}, {"capacity": 3900.0, "source": 10, "target": 13}, {"capacity": 200.0, "source": 11, "target": 10}, {"capacity": 350.0, "source": 11, "target": 16}, {"capacity": 350.0, "source": 11, "target": 18}, {"capacity": 300.0, "source": 15, "target": 14}, {"capacity": 400.0, "source": 15, "target": 18}, {"capacity": 150.0, "source": 25, "target": 97}, {"capacity": 100.0, "source": 91, "target": 66}, {"capacity": 150.0, "source": 91, "target": 90}, {"capacity": 550.0, "source": 91, "target": 97}, {"capacity": 550.0, "source": 91, "target": 117}, {"capacity": 50.0, "source": 98, "target": 90}, {"capacity": 150.0, "source": 98, "target": 97}, {"capacity": 2900.0, "source": 32, "target": 33}, {"capacity": 2250.0, "source": 32, "target": 35}, {"capacity": 50.0, "source": 114, "target": 66}, {"capacity": 100.0, "source": 114, "target": 117}, {"capacity": 450.0, "source": 4, "target": 3}, {"capacity": 450.0, "source": 4, "target": 48}, {"capacity": 650.0, "source": 5, "target": 3}, {"capacity": 600.0, "source": 5, "target": 6}, {"capacity": 400.0, "source": 141, "target": 134}, {"capacity": 450.0, "source": 141, "target": 140}, {"capacity": 2900.0, "source": 76, "target": 68}, {"capacity": 550.0, "source": 76, "target": 72}, {"capacity": 1700.0, "source": 76, "target": 92}, {"capacity": 400.0, "source": 70, "target": 69}, {"capacity": 100.0, "source": 70, "target": 71}, {"capacity": 1200.0, "source": 49, "target": 157}, {"capacity": 1500.0, "source": 49, "target": 135}, {"capacity": 1150.0, "source": 136, "target": 134}, {"capacity": 1000.0, "source": 136, "target": 135}, {"capacity": 450.0, "source": 150, "target": 61}, {"capacity": 100.0, "source": 150, "target": 152}, {"capacity": 350.0, "source": 150, "target": 154}, {"capacity": 950.0, "source": 150, "target": 151}, {"capacity": 3500.0, "source": 13, "target": 10}, {"capacity": 3850.0, "source": 13, "target": 12}, {"capacity": 350.0, "source": 16, "target": 11}, {"capacity": 300.0, "source": 16, "target": 17}, {"capacity": 250.0, "source": 14, "target": 17}, {"capacity": 250.0, "source": 14, "target": 15}, {"capacity": 50.0, "source": 66, "target": 114}, {"capacity": 50.0, "source": 66, "target": 91}, {"capacity": 50.0, "source": 90, "target": 98}, {"capacity": 150.0, "source": 90, "target": 91}, {"capacity": 2250.0, "source": 33, "target": 32}, {"capacity": 1350.0, "source": 33, "target": 34}, {"capacity": 1950.0, "source": 33, "target": 36}, {"capacity": 300.0, "source": 33, "target": 39}, {"capacity": 400.0, "source": 48, "target": 4}, {"capacity": 350.0, "source": 48, "target": 47}, {"capacity": 600.0, "source": 6, "target": 5}, {"capacity": 650.0, "source": 6, "target": 157}, {"capacity": 850.0, "source": 134, "target": 136}, {"capacity": 700.0, "source": 134, "target": 137}, {"capacity": 400.0, "source": 134, "target": 141}, {"capacity": 250.0, "source": 72, "target": 73}, {"capacity": 200.0, "source": 72, "target": 74}, {"capacity": 650.0, "source": 72, "target": 76}, {"capacity": 750.0, "source": 92, "target": 75}, {"capacity": 2350.0, "source": 92, "target": 76}, {"capacity": 850.0, "source": 92, "target": 93}, {"capacity": 250.0, "source": 71, "target": 70}, {"capacity": 700.0, "source": 157, "target": 6}, {"capacity": 1400.0, "source": 157, "target": 49}, {"capacity": 1000.0, "source": 157, "target": 142}, {"capacity": 450.0, "source": 157, "target": 156}, {"capacity": 400.0, "source": 61, "target": 155}, {"capacity": 500.0, "source": 61, "target": 150}, {"capacity": 300.0, "source": 154, "target": 150}, {"capacity": 300.0, "source": 154, "target": 153}, {"capacity": 3450.0, "source": 12, "target": 13}, {"capacity": 3750.0, "source": 12, "target": 30}, {"capacity": 200.0, "source": 17, "target": 14}, {"capacity": 300.0, "source": 17, "target": 16}, {"capacity": 50.0, "source": 34, "target": 31}, {"capacity": 1200.0, "source": 34, "target": 33}, {"capacity": 1150.0, "source": 34, "target": 84}, {"capacity": 1650.0, "source": 36, "target": 33}, {"capacity": 1850.0, "source": 36, "target": 37}, {"capacity": 250.0, "source": 39, "target": 33}, {"capacity": 250.0, "source": 39, "target": 38}, {"capacity": 300.0, "source": 47, "target": 46}, {"capacity": 350.0, "source": 47, "target": 48}, {"capacity": 600.0, "source": 137, "target": 46}, {"capacity": 550.0, "source": 137, "target": 134}, {"capacity": 150.0, "source": 73, "target": 72}, {"capacity": 50.0, "source": 73, "target": 74}, {"capacity": 300.0, "source": 74, "target": 72}, {"capacity": 50.0, "source": 74, "target": 73}, {"capacity": 850.0, "source": 75, "target": 92}, {"capacity": 600.0, "source": 75, "target": 111}, {"capacity": 1350.0, "source": 93, "target": 92}, {"capacity": 700.0, "source": 93, "target": 94}, {"capacity": 1000.0, "source": 142, "target": 157}, {"capacity": 800.0, "source": 142, "target": 143}, {"capacity": 350.0, "source": 156, "target": 155}, {"capacity": 550.0, "source": 156, "target": 157}, {"capacity": 200.0, "source": 155, "target": 60}, {"capacity": 400.0, "source": 155, "target": 61}, {"capacity": 450.0, "source": 155, "target": 156}, {"capacity": 200.0, "source": 153, "target": 60}, {"capacity": 250.0, "source": 153, "target": 154}, {"capacity": 3500.0, "source": 30, "target": 12}, {"capacity": 1150.0, "source": 30, "target": 84}, {"capacity": 3650.0, "source": 30, "target": 131}, {"capacity": 100.0, "source": 31, "target": 34}, {"capacity": 50.0, "source": 31, "target": 84}, {"capacity": 1200.0, "source": 84, "target": 30}, {"capacity": 50.0, "source": 84, "target": 31}, {"capacity": 1100.0, "source": 84, "target": 34}, {"capacity": 1550.0, "source": 37, "target": 36}, {"capacity": 1800.0, "source": 37, "target": 83}, {"capacity": 300.0, "source": 38, "target": 83}, {"capacity": 150.0, "source": 38, "target": 39}, {"capacity": 300.0, "source": 46, "target": 41}, {"capacity": 200.0, "source": 46, "target": 45}, {"capacity": 300.0, "source": 46, "target": 47}, {"capacity": 500.0, "source": 46, "target": 137}, {"capacity": 700.0, "source": 111, "target": 75}, {"capacity": 450.0, "source": 111, "target": 110}, {"capacity": 500.0, "source": 94, "target": 88}, {"capacity": 900.0, "source": 94, "target": 93}, {"capacity": 100.0, "source": 94, "target": 95}, {"capacity": 550.0, "source": 143, "target": 87}, {"capacity": 800.0, "source": 143, "target": 142}, {"capacity": 150.0, "source": 60, "target": 153}, {"capacity": 300.0, "source": 60, "target": 155}, {"capacity": 3500.0, "source": 131, "target": 30}, {"capacity": 3300.0, "source": 131, "target": 124}, {"capacity": 200.0, "source": 131, "target": 130}, {"capacity": 1550.0, "source": 83, "target": 37}, {"capacity": 150.0, "source": 83, "target": 38}, {"capacity": 1750.0, "source": 83, "target": 53}, {"capacity": 250.0, "source": 41, "target": 44}, {"capacity": 250.0, "source": 41, "target": 46}, {"capacity": 150.0, "source": 45, "target": 44}, {"capacity": 150.0, "source": 45, "target": 46}, {"capacity": 200.0, "source": 44, "target": 41}, {"capacity": 50.0, "source": 44, "target": 45}, {"capacity": 400.0, "source": 87, "target": 42}, {"capacity": 550.0, "source": 87, "target": 143}, {"capacity": 300.0, "source": 42, "target": 43}, {"capacity": 450.0, "source": 42, "target": 87}, {"capacity": 200.0, "source": 43, "target": 40}, {"capacity": 300.0, "source": 43, "target": 42}, {"capacity": 150.0, "source": 40, "target": 43}, {"capacity": 50.0, "source": 110, "target": 95}, {"capacity": 300.0, "source": 110, "target": 113}, {"capacity": 600.0, "source": 110, "target": 111}, {"capacity": 250.0, "source": 88, "target": 89}, {"capacity": 450.0, "source": 88, "target": 94}, {"capacity": 250.0, "source": 95, "target": 94}, {"capacity": 50.0, "source": 95, "target": 110}, {"capacity": 450.0, "source": 113, "target": 110}, {"capacity": 100.0, "source": 113, "target": 112}, {"capacity": 300.0, "source": 89, "target": 88}, {"capacity": 50.0, "source": 89, "target": 115}, {"capacity": 950.0, "source": 124, "target": 50}, {"capacity": 2400.0, "source": 124, "target": 122}, {"capacity": 3300.0, "source": 124, "target": 131}, {"capacity": 150.0, "source": 130, "target": 125}, {"capacity": 100.0, "source": 130, "target": 131}, {"capacity": 800.0, "source": 53, "target": 52}, {"capacity": 1000.0, "source": 53, "target": 58}, {"capacity": 1450.0, "source": 53, "target": 83}, {"capacity": 300.0, "source": 53, "target": 54}, {"capacity": 700.0, "source": 50, "target": 57}, {"capacity": 450.0, "source": 50, "target": 51}, {"capacity": 1100.0, "source": 50, "target": 124}, {"capacity": 50.0, "source": 50, "target": 125}, {"capacity": 2350.0, "source": 122, "target": 129}, {"capacity": 2150.0, "source": 122, "target": 124}, {"capacity": 50.0, "source": 125, "target": 50}, {"capacity": 50.0, "source": 125, "target": 130}, {"capacity": 650.0, "source": 52, "target": 28}, {"capacity": 700.0, "source": 52, "target": 53}, {"capacity": 900.0, "source": 58, "target": 53}, {"capacity": 1000.0, "source": 58, "target": 59}, {"capacity": 250.0, "source": 54, "target": 53}, {"capacity": 100.0, "source": 54, "target": 55}, {"capacity": 300.0, "source": 112, "target": 113}, {"capacity": 50.0, "source": 112, "target": 115}, {"capacity": 150.0, "source": 115, "target": 89}, {"capacity": 50.0, "source": 115, "target": 112}, {"capacity": 850.0, "source": 57, "target": 50}, {"capacity": 850.0, "source": 57, "target": 56}, {"capacity": 650.0, "source": 57, "target": 128}, {"capacity": 200.0, "source": 57, "target": 123}, {"capacity": 500.0, "source": 51, "target": 50}, {"capacity": 250.0, "source": 51, "target": 149}, {"capacity": 2150.0, "source": 129, "target": 122}, {"capacity": 150.0, "source": 129, "target": 123}, {"capacity": 2250.0, "source": 129, "target": 127}, {"capacity": 100.0, "source": 129, "target": 128}, {"capacity": 1050.0, "source": 56, "target": 57}, {"capacity": 850.0, "source": 56, "target": 59}, {"capacity": 450.0, "source": 128, "target": 57}, {"capacity": 500.0, "source": 128, "target": 127}, {"capacity": 100.0, "source": 128, "target": 129}, {"capacity": 250.0, "source": 123, "target": 57}, {"capacity": 200.0, "source": 123, "target": 129}, {"capacity": 350.0, "source": 149, "target": 51}, {"capacity": 50.0, "source": 149, "target": 148}, {"capacity": 2600.0, "source": 127, "target": 78}, {"capacity": 250.0, "source": 127, "target": 128}, {"capacity": 2000.0, "source": 127, "target": 129}, {"capacity": 950.0, "source": 59, "target": 56}, {"capacity": 850.0, "source": 59, "target": 58}, {"capacity": 150.0, "source": 148, "target": 149}, {"capacity": 1800.0, "source": 78, "target": 62}, {"capacity": 700.0, "source": 78, "target": 120}, {"capacity": 2150.0, "source": 78, "target": 127}, {"capacity": 1500.0, "source": 62, "target": 78}, {"capacity": 1650.0, "source": 62, "target": 79}, {"capacity": 550.0, "source": 120, "target": 78}, {"capacity": 550.0, "source": 120, "target": 96}, {"capacity": 1450.0, "source": 79, "target": 62}, {"capacity": 450.0, "source": 79, "target": 63}, {"capacity": 1050.0, "source": 79, "target": 99}, {"capacity": 450.0, "source": 96, "target": 120}, {"capacity": 350.0, "source": 96, "target": 126}, {"capacity": 450.0, "source": 28, "target": 22}, {"capacity": 500.0, "source": 28, "target": 52}, {"capacity": 150.0, "source": 55, "target": 54}, {"capacity": 200.0, "source": 63, "target": 64}, {"capacity": 400.0, "source": 63, "target": 79}, {"capacity": 900.0, "source": 99, "target": 79}, {"capacity": 850.0, "source": 99, "target": 121}, {"capacity": 200.0, "source": 126, "target": 65}, {"capacity": 350.0, "source": 126, "target": 96}, {"capacity": 350.0, "source": 22, "target": 28}, {"capacity": 150.0, "source": 22, "target": 29}, {"capacity": 100.0, "source": 22, "target": 23}, {"capacity": 200.0, "source": 64, "target": 63}, {"capacity": 50.0, "source": 64, "target": 65}, {"capacity": 800.0, "source": 121, "target": 99}, {"capacity": 650.0, "source": 121, "target": 144}, {"capacity": 50.0, "source": 65, "target": 64}, {"capacity": 250.0, "source": 65, "target": 126}, {"capacity": 150.0, "source": 29, "target": 22}, {"capacity": 50.0, "source": 29, "target": 23}, {"capacity": 100.0, "source": 23, "target": 22}, {"capacity": 50.0, "source": 23, "target": 29}, {"capacity": 550.0, "source": 144, "target": 121}, {"capacity": 400.0, "source": 144, "target": 145}, {"capacity": 400.0, "source": 145, "target": 144}, {"capacity": 200.0, "source": 145, "target": 146}, {"capacity": 300.0, "source": 146, "target": 145}, {"capacity": 100.0, "source": 146, "target": 147}, {"capacity": 150.0, "source": 147, "target": 146}]} -------------------------------------------------------------------------------- /topologies/paths/path-form/B4.json-4-paths_edge-disjoint-True_dist-metric-min-hop-dict.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/topologies/paths/path-form/B4.json-4-paths_edge-disjoint-True_dist-metric-min-hop-dict.pkl -------------------------------------------------------------------------------- /topologies/paths/path-form/UsCarrier.json-4-paths_edge-disjoint-True_dist-metric-min-hop-dict.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/topologies/paths/path-form/UsCarrier.json-4-paths_edge-disjoint-True_dist-metric-min-hop-dict.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_0_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_0_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_10_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_10_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_11_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_11_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_12_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_12_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_13_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_13_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_14_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_14_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_15_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_15_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_16_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_16_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_17_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_17_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_18_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_18_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_19_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_19_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_1_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_1_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_20_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_20_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_21_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_21_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_22_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_22_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_23_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_23_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_24_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_24_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_25_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_25_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_26_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_26_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_27_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_27_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_28_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_28_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_29_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_29_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_2_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_2_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_30_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_30_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_31_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_31_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_32_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_32_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_33_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_33_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_34_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_34_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_35_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_35_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_3_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_3_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_4_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_4_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_5_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_5_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_6_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_6_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_7_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_7_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_8_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_8_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/real/B4.json_real_9_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/real/B4.json_real_9_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_0_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_0_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_10_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_10_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_11_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_11_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_12_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_12_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_13_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_13_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_14_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_14_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_15_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_15_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_16_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_16_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_17_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_17_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_18_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_18_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_19_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_19_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_1_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_1_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_20_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_20_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_21_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_21_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_22_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_22_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_23_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_23_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_24_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_24_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_25_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_25_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_26_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_26_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_27_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_27_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_28_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_28_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_29_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_29_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_2_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_2_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_30_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_30_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_31_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_31_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_32_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_32_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_33_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_33_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_34_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_34_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_35_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_35_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_3_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_3_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_4_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_4_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_5_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_5_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_6_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_6_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_7_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_7_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_8_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_8_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/ASN2k.json_toy_9_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/ASN2k.json_toy_9_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_0_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_0_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_10_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_10_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_11_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_11_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_12_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_12_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_13_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_13_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_14_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_14_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_15_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_15_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_16_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_16_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_17_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_17_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_18_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_18_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_19_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_19_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_1_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_1_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_20_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_20_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_21_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_21_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_22_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_22_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_23_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_23_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_24_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_24_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_25_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_25_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_26_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_26_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_27_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_27_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_28_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_28_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_29_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_29_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_2_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_2_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_30_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_30_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_31_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_31_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_32_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_32_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_33_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_33_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_34_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_34_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_35_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_35_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_3_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_3_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_4_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_4_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_5_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_5_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_6_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_6_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_7_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_7_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_8_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_8_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/Kdl.json_toy_9_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/Kdl.json_toy_9_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_0_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_0_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_10_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_10_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_11_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_11_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_12_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_12_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_13_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_13_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_14_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_14_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_15_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_15_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_16_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_16_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_17_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_17_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_18_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_18_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_19_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_19_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_1_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_1_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_20_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_20_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_21_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_21_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_22_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_22_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_23_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_23_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_24_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_24_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_25_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_25_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_26_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_26_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_27_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_27_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_28_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_28_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_29_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_29_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_2_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_2_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_30_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_30_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_31_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_31_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_32_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_32_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_33_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_33_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_34_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_34_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_35_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_35_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_3_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_3_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_4_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_4_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_5_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_5_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_6_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_6_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_7_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_7_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_8_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_8_1.0_traffic-matrix.pkl -------------------------------------------------------------------------------- /traffic-matrices/toy/UsCarrier.json_toy_9_1.0_traffic-matrix.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harvard-cns/teal/8076c9924bbfd37542b22717c20911c0d4419d00/traffic-matrices/toy/UsCarrier.json_toy_9_1.0_traffic-matrix.pkl --------------------------------------------------------------------------------