pycombinatorial 2.1.0__tar.gz → 2.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/PKG-INFO +4 -2
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/README.md +3 -1
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/__init__.py +3 -1
- pycombinatorial-2.1.2/pyCombinatorial/algorithm/ins_f.py +126 -0
- pycombinatorial-2.1.2/pyCombinatorial/algorithm/opt_or.py +192 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/rr.py +2 -4
- pycombinatorial-2.1.2/pyCombinatorial/algorithm/ssi.py +143 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pycombinatorial.egg-info/PKG-INFO +4 -2
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pycombinatorial.egg-info/SOURCES.txt +2 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/setup.py +1 -1
- pycombinatorial-2.1.0/pyCombinatorial/algorithm/ins_f.py +0 -112
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/LICENSE +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/__init__.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/aco.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/alns.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/bb.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/bf.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/bhk.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/brkga.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/bt.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/christofides.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/conc_hull.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/conv_hull.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/cw.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/eln.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/eo.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/frnn.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/ga.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/gksp.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/grasp.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/hpn.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/ins_c.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/ins_n.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/ins_r.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/ksp.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/lns.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/mf.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/nn.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/opt_2.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/opt_2_5.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/opt_2_5s.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/opt_2s.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/opt_3.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/opt_3s.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/opt_4.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/opt_4s.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/opt_5.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/opt_5s.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/rl_double_ql.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/rl_ql.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/rl_sarsa.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/rt.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/s_gui.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/s_itr.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/s_sct.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/s_shc.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/s_tabu.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/s_vns.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/sa.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/som.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/spfc_h.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/spfc_m.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/spfc_s.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/swp.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/tat.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/tbb.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/algorithm/zs.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/utils/__init__.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/utils/graphs.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pyCombinatorial/utils/util.py +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pycombinatorial.egg-info/dependency_links.txt +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pycombinatorial.egg-info/requires.txt +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pycombinatorial.egg-info/top_level.txt +0 -0
- {pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pycombinatorial
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: A library to solve TSP (Travelling Salesman Problem) using Exact Algorithms, Heuristics, Metaheuristics and Reinforcement Learning
|
|
5
5
|
Home-page: https://github.com/Valdecy/pyCombinatorial
|
|
6
6
|
Author: Valdecy Pereira
|
|
@@ -15,7 +15,7 @@ License-File: LICENSE
|
|
|
15
15
|
|
|
16
16
|
**pyCombinatorial** is a Python-based library designed to tackle the classic Travelling Salesman Problem (TSP) through a diverse set of **Exact Algorithms**, **Heuristics**, **Metaheuristics** and **Reinforcement Learning**. It brings together both well-established and cutting-edge methodologies, offering end-users a flexible toolkit to generate high-quality solutions for TSP instances of various sizes and complexities.
|
|
17
17
|
|
|
18
|
-
Techniques: **2-opt**; **2.5-opt**; **3-opt**; **4-opt**; **5-opt**; **2-opt Stochastic**; **2.5-opt Stochastic**; **3-opt Stochastic**; **4-opt Stochastic**; **5-opt Stochastic**; **Ant Colony Optimization**; **Adaptive Large Neighborhood Search**; **Bellman-Held-Karp Exact Algorithm**; **Bitonic Tour**; **Branch & Bound**; **BRKGA** (Biased Random Key Genetic Algorithm); **Brute Force**; **Cheapest Insertion**; **Christofides Algorithm**; **Clarke & Wright** (Savings Heuristic); **Concave Hull Algorithm**; **Convex Hull Algorithm**; **Elastic Net**; **Extremal Optimization**; **Farthest Insertion**; **FRNN** (Fixed Radius Near Neighbor); **Genetic Algorithm**; **GRASP** (Greedy Randomized Adaptive Search Procedure); **Greedy Karp-Steele Patching**; **Guided Search**; **Hopfield Network**; **Iterated Search**; **Karp-Steele Patching**; **Large Neighborhood Search**; **Multifragment Heuristic**; **Nearest Insertion**; **Nearest Neighbour**; **Random Insertion**; **Random Tour**; **RL Q-Learning**; **RL Double Q-Learning**; **RL S.A.R.S.A** (State Action Reward State Action); **Ruin & Recreate**; **Scatter Search**; **Simulated Annealing**; **SOM** (Self Organizing Maps); **Space Filling Curve** (Hilbert); **Space Filling Curve** (Morton); **Space Filling Curve** (Sierpinski); **Stochastic Hill Climbing**; **Sweep**; **Tabu Search**; **Truncated Branch & Bound**; **Twice-Around the Tree Algorithm** (Double Tree Algorithm); **Variable Neighborhood Search**; **Zero Suffix Method**.
|
|
18
|
+
Techniques: **2-opt**; **2.5-opt**; **3-opt**; **4-opt**; **5-opt**; **Or-opt**; **2-opt Stochastic**; **2.5-opt Stochastic**; **3-opt Stochastic**; **4-opt Stochastic**; **5-opt Stochastic**; **Ant Colony Optimization**; **Adaptive Large Neighborhood Search**; **Bellman-Held-Karp Exact Algorithm**; **Bitonic Tour**; **Branch & Bound**; **BRKGA** (Biased Random Key Genetic Algorithm); **Brute Force**; **Cheapest Insertion**; **Christofides Algorithm**; **Clarke & Wright** (Savings Heuristic); **Concave Hull Algorithm**; **Convex Hull Algorithm**; **Elastic Net**; **Extremal Optimization**; **Farthest Insertion**; **FRNN** (Fixed Radius Near Neighbor); **Genetic Algorithm**; **GRASP** (Greedy Randomized Adaptive Search Procedure); **Greedy Karp-Steele Patching**; **Guided Search**; **Hopfield Network**; **Iterated Search**; **Karp-Steele Patching**; **Large Neighborhood Search**; **Multifragment Heuristic**; **Nearest Insertion**; **Nearest Neighbour**; **Random Insertion**; **Random Tour**; **RL Q-Learning**; **RL Double Q-Learning**; **RL S.A.R.S.A** (State Action Reward State Action); **Ruin & Recreate**; **Scatter Search**; **Simulated Annealing**; **SOM** (Self Organizing Maps); **Space Filling Curve** (Hilbert); **Space Filling Curve** (Morton); **Space Filling Curve** (Sierpinski); **Spectral Seriation Initializer**; **Stochastic Hill Climbing**; **Sweep**; **Tabu Search**; **Truncated Branch & Bound**; **Twice-Around the Tree Algorithm** (Double Tree Algorithm); **Variable Neighborhood Search**; **Zero Suffix Method**.
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
21
21
|
|
|
@@ -76,6 +76,7 @@ print('Total Distance: ', round(distance, 2))
|
|
|
76
76
|
- 3-opt ([ Colab Demo ](https://colab.research.google.com/drive/1iAZLawLBZ-7yaPCyobMtel1SvBamxtjL?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
77
77
|
- 4-opt ([ Colab Demo ](https://colab.research.google.com/drive/1N8HKhVY4s20sfqo8IWIaCY-NHVk6gARS?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
78
78
|
- 5-opt ([ Colab Demo ](https://colab.research.google.com/drive/15Qrk-7H4oRaTR77ADvwkiN0sLvycgFDH?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
79
|
+
- Or-opt ([ Colab Demo ](https://colab.research.google.com/drive/1p7JwrFLH83ZroCzIweXLCAXYgA3FKAI0?usp=sharing)) ( [ Paper ](https://doi.org/10.1057/palgrave.jors.2602160))
|
|
79
80
|
- 2-opt Stochastic ([ Colab Demo ](https://colab.research.google.com/drive/1xTm__7OwQVC_KX2b-eExLGgG1DgnJ10a?usp=sharing)) ( [ Paper ](https://doi.org/10.1016/j.trpro.2014.10.001))
|
|
80
81
|
- 2.5-opt Stochastic ([ Colab Demo ](https://colab.research.google.com/drive/16W_QqJ1PebVgqUx8NFOSS5kG3DsJ51UQ?usp=sharing)) ( [ Paper ](https://doi.org/10.1007/s10955-007-9382-1))
|
|
81
82
|
- 3-opt Stochastic ([ Colab Demo ](https://colab.research.google.com/drive/1A5lPW6BSDD2rLNDlnpQo44U8jwKcAGXL?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
@@ -120,6 +121,7 @@ print('Total Distance: ', round(distance, 2))
|
|
|
120
121
|
- Space Filling Curve (Hilbert) ([ Colab Demo ](https://colab.research.google.com/drive/1FXzWrUBjdbJBngRFHv66CZw5pFN3yOs8?usp=sharing)) ( [ Paper ](https://doi.org/10.1016/0960-0779(95)80046-J))
|
|
121
122
|
- Space Filling Curve (Morton) ([ Colab Demo ](https://colab.research.google.com/drive/1Z13kXyi7eaNQbBUmhvwuQjY4VaUfGVbs?usp=sharing)) ( [ Paper ](https://dominoweb.draco.res.ibm.com/reports/Morton1966.pdf))
|
|
122
123
|
- Space Filling Curve (Sierpinski) ([ Colab Demo ](https://colab.research.google.com/drive/1w-Zptd5kOryCwvQ0qSNBNhPXC61c8QXF?usp=sharing)) ( [ Paper ](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.67.9061&rep=rep1&type=pdf))
|
|
124
|
+
- Spectral Seriation Initializer ([ Colab Demo ](https://colab.research.google.com/drive/1lG0pYxASU75qh0jK-A_eMCoPpCWv0I4V?usp=sharing)) ( [ Paper ](https://doi.org/10.1137/S009753979528577))
|
|
123
125
|
- Stochastic Hill Climbing ([ Colab Demo ](https://colab.research.google.com/drive/1_wP6vg4JoRHGItGxEtXcf9Y9OuuoDlDl?usp=sharing)) ( [ Paper ](http://aima.cs.berkeley.edu/))
|
|
124
126
|
- Sweep ([ Colab Demo ](https://colab.research.google.com/drive/1AkAn4yeomAp6POBslk3Asd6OrxfBrHT7?usp=sharing)) ( [ Paper ](http://dx.doi.org/10.1287/opre.22.2.340))
|
|
125
127
|
- Tabu Search ([ Colab Demo ](https://colab.research.google.com/drive/1SRwQrBaxkKk18SDvQPy--0yNRWdl6Y1G?usp=sharing)) ( [ Paper ](https://doi.org/10.1287/ijoc.1.3.190))
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**pyCombinatorial** is a Python-based library designed to tackle the classic Travelling Salesman Problem (TSP) through a diverse set of **Exact Algorithms**, **Heuristics**, **Metaheuristics** and **Reinforcement Learning**. It brings together both well-established and cutting-edge methodologies, offering end-users a flexible toolkit to generate high-quality solutions for TSP instances of various sizes and complexities.
|
|
6
6
|
|
|
7
|
-
Techniques: **2-opt**; **2.5-opt**; **3-opt**; **4-opt**; **5-opt**; **2-opt Stochastic**; **2.5-opt Stochastic**; **3-opt Stochastic**; **4-opt Stochastic**; **5-opt Stochastic**; **Ant Colony Optimization**; **Adaptive Large Neighborhood Search**; **Bellman-Held-Karp Exact Algorithm**; **Bitonic Tour**; **Branch & Bound**; **BRKGA** (Biased Random Key Genetic Algorithm); **Brute Force**; **Cheapest Insertion**; **Christofides Algorithm**; **Clarke & Wright** (Savings Heuristic); **Concave Hull Algorithm**; **Convex Hull Algorithm**; **Elastic Net**; **Extremal Optimization**; **Farthest Insertion**; **FRNN** (Fixed Radius Near Neighbor); **Genetic Algorithm**; **GRASP** (Greedy Randomized Adaptive Search Procedure); **Greedy Karp-Steele Patching**; **Guided Search**; **Hopfield Network**; **Iterated Search**; **Karp-Steele Patching**; **Large Neighborhood Search**; **Multifragment Heuristic**; **Nearest Insertion**; **Nearest Neighbour**; **Random Insertion**; **Random Tour**; **RL Q-Learning**; **RL Double Q-Learning**; **RL S.A.R.S.A** (State Action Reward State Action); **Ruin & Recreate**; **Scatter Search**; **Simulated Annealing**; **SOM** (Self Organizing Maps); **Space Filling Curve** (Hilbert); **Space Filling Curve** (Morton); **Space Filling Curve** (Sierpinski); **Stochastic Hill Climbing**; **Sweep**; **Tabu Search**; **Truncated Branch & Bound**; **Twice-Around the Tree Algorithm** (Double Tree Algorithm); **Variable Neighborhood Search**; **Zero Suffix Method**.
|
|
7
|
+
Techniques: **2-opt**; **2.5-opt**; **3-opt**; **4-opt**; **5-opt**; **Or-opt**; **2-opt Stochastic**; **2.5-opt Stochastic**; **3-opt Stochastic**; **4-opt Stochastic**; **5-opt Stochastic**; **Ant Colony Optimization**; **Adaptive Large Neighborhood Search**; **Bellman-Held-Karp Exact Algorithm**; **Bitonic Tour**; **Branch & Bound**; **BRKGA** (Biased Random Key Genetic Algorithm); **Brute Force**; **Cheapest Insertion**; **Christofides Algorithm**; **Clarke & Wright** (Savings Heuristic); **Concave Hull Algorithm**; **Convex Hull Algorithm**; **Elastic Net**; **Extremal Optimization**; **Farthest Insertion**; **FRNN** (Fixed Radius Near Neighbor); **Genetic Algorithm**; **GRASP** (Greedy Randomized Adaptive Search Procedure); **Greedy Karp-Steele Patching**; **Guided Search**; **Hopfield Network**; **Iterated Search**; **Karp-Steele Patching**; **Large Neighborhood Search**; **Multifragment Heuristic**; **Nearest Insertion**; **Nearest Neighbour**; **Random Insertion**; **Random Tour**; **RL Q-Learning**; **RL Double Q-Learning**; **RL S.A.R.S.A** (State Action Reward State Action); **Ruin & Recreate**; **Scatter Search**; **Simulated Annealing**; **SOM** (Self Organizing Maps); **Space Filling Curve** (Hilbert); **Space Filling Curve** (Morton); **Space Filling Curve** (Sierpinski); **Spectral Seriation Initializer**; **Stochastic Hill Climbing**; **Sweep**; **Tabu Search**; **Truncated Branch & Bound**; **Twice-Around the Tree Algorithm** (Double Tree Algorithm); **Variable Neighborhood Search**; **Zero Suffix Method**.
|
|
8
8
|
|
|
9
9
|
## Usage
|
|
10
10
|
|
|
@@ -65,6 +65,7 @@ print('Total Distance: ', round(distance, 2))
|
|
|
65
65
|
- 3-opt ([ Colab Demo ](https://colab.research.google.com/drive/1iAZLawLBZ-7yaPCyobMtel1SvBamxtjL?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
66
66
|
- 4-opt ([ Colab Demo ](https://colab.research.google.com/drive/1N8HKhVY4s20sfqo8IWIaCY-NHVk6gARS?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
67
67
|
- 5-opt ([ Colab Demo ](https://colab.research.google.com/drive/15Qrk-7H4oRaTR77ADvwkiN0sLvycgFDH?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
68
|
+
- Or-opt ([ Colab Demo ](https://colab.research.google.com/drive/1p7JwrFLH83ZroCzIweXLCAXYgA3FKAI0?usp=sharing)) ( [ Paper ](https://doi.org/10.1057/palgrave.jors.2602160))
|
|
68
69
|
- 2-opt Stochastic ([ Colab Demo ](https://colab.research.google.com/drive/1xTm__7OwQVC_KX2b-eExLGgG1DgnJ10a?usp=sharing)) ( [ Paper ](https://doi.org/10.1016/j.trpro.2014.10.001))
|
|
69
70
|
- 2.5-opt Stochastic ([ Colab Demo ](https://colab.research.google.com/drive/16W_QqJ1PebVgqUx8NFOSS5kG3DsJ51UQ?usp=sharing)) ( [ Paper ](https://doi.org/10.1007/s10955-007-9382-1))
|
|
70
71
|
- 3-opt Stochastic ([ Colab Demo ](https://colab.research.google.com/drive/1A5lPW6BSDD2rLNDlnpQo44U8jwKcAGXL?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
@@ -109,6 +110,7 @@ print('Total Distance: ', round(distance, 2))
|
|
|
109
110
|
- Space Filling Curve (Hilbert) ([ Colab Demo ](https://colab.research.google.com/drive/1FXzWrUBjdbJBngRFHv66CZw5pFN3yOs8?usp=sharing)) ( [ Paper ](https://doi.org/10.1016/0960-0779(95)80046-J))
|
|
110
111
|
- Space Filling Curve (Morton) ([ Colab Demo ](https://colab.research.google.com/drive/1Z13kXyi7eaNQbBUmhvwuQjY4VaUfGVbs?usp=sharing)) ( [ Paper ](https://dominoweb.draco.res.ibm.com/reports/Morton1966.pdf))
|
|
111
112
|
- Space Filling Curve (Sierpinski) ([ Colab Demo ](https://colab.research.google.com/drive/1w-Zptd5kOryCwvQ0qSNBNhPXC61c8QXF?usp=sharing)) ( [ Paper ](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.67.9061&rep=rep1&type=pdf))
|
|
113
|
+
- Spectral Seriation Initializer ([ Colab Demo ](https://colab.research.google.com/drive/1lG0pYxASU75qh0jK-A_eMCoPpCWv0I4V?usp=sharing)) ( [ Paper ](https://doi.org/10.1137/S009753979528577))
|
|
112
114
|
- Stochastic Hill Climbing ([ Colab Demo ](https://colab.research.google.com/drive/1_wP6vg4JoRHGItGxEtXcf9Y9OuuoDlDl?usp=sharing)) ( [ Paper ](http://aima.cs.berkeley.edu/))
|
|
113
115
|
- Sweep ([ Colab Demo ](https://colab.research.google.com/drive/1AkAn4yeomAp6POBslk3Asd6OrxfBrHT7?usp=sharing)) ( [ Paper ](http://dx.doi.org/10.1287/opre.22.2.340))
|
|
114
116
|
- Tabu Search ([ Colab Demo ](https://colab.research.google.com/drive/1SRwQrBaxkKk18SDvQPy--0yNRWdl6Y1G?usp=sharing)) ( [ Paper ](https://doi.org/10.1287/ijoc.1.3.190))
|
|
@@ -29,6 +29,7 @@ from .opt_2_5 import local_search_2h_opt
|
|
|
29
29
|
from .opt_3 import local_search_3_opt
|
|
30
30
|
from .opt_4 import local_search_4_opt
|
|
31
31
|
from .opt_5 import local_search_5_opt
|
|
32
|
+
from .opt_or import local_search_or_opt
|
|
32
33
|
from .opt_2s import local_search_2_opt_stochastic
|
|
33
34
|
from .opt_2_5s import local_search_2h_opt_stochastic
|
|
34
35
|
from .opt_3s import local_search_3_opt_stochastic
|
|
@@ -37,8 +38,8 @@ from .opt_5s import local_search_5_opt_stochastic
|
|
|
37
38
|
from .rl_double_ql import double_q_learning
|
|
38
39
|
from .rl_ql import q_learning
|
|
39
40
|
from .rl_sarsa import sarsa
|
|
40
|
-
from .rt import random_tour
|
|
41
41
|
from .rr import ruin_and_recreate
|
|
42
|
+
from .rt import random_tour
|
|
42
43
|
from .s_gui import guided_search
|
|
43
44
|
from .s_itr import iterated_search
|
|
44
45
|
from .s_sct import scatter_search
|
|
@@ -50,6 +51,7 @@ from .som import self_organizing_maps
|
|
|
50
51
|
from .spfc_h import space_filling_curve_h
|
|
51
52
|
from .spfc_m import space_filling_curve_m
|
|
52
53
|
from .spfc_s import space_filling_curve_s
|
|
54
|
+
from .ssi import spectral_seriation_initializer
|
|
53
55
|
from .swp import sweep
|
|
54
56
|
from .tat import tat_algorithm
|
|
55
57
|
from .tbb import truncated_branch_and_bound
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
# Created by: Prof. Valdecy Pereira, D.Sc.
|
|
3
|
+
# UFF - Universidade Federal Fluminense (Brazil)
|
|
4
|
+
# email: valdecy.pereira@gmail.com
|
|
5
|
+
|
|
6
|
+
# Lesson: pyCombinatorial - Farthest Insertion
|
|
7
|
+
|
|
8
|
+
# GitHub Repository: <https://github.com/Valdecy>
|
|
9
|
+
|
|
10
|
+
############################################################################
|
|
11
|
+
|
|
12
|
+
import copy
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
############################################################################
|
|
16
|
+
|
|
17
|
+
# Function: Tour Distance
|
|
18
|
+
def distance_calc(distance_matrix, city_tour):
|
|
19
|
+
distance = 0
|
|
20
|
+
for k in range(0, len(city_tour[0]) - 1):
|
|
21
|
+
m = k + 1
|
|
22
|
+
distance = distance + distance_matrix[city_tour[0][k] - 1, city_tour[0][m] - 1]
|
|
23
|
+
return distance
|
|
24
|
+
|
|
25
|
+
############################################################################
|
|
26
|
+
|
|
27
|
+
# Function: 2_opt
|
|
28
|
+
def local_search_2_opt(distance_matrix, city_tour, recursive_seeding = -1, verbose = True):
|
|
29
|
+
if (recursive_seeding < 0):
|
|
30
|
+
count = -2
|
|
31
|
+
else:
|
|
32
|
+
count = 0
|
|
33
|
+
city_list = copy.deepcopy(city_tour)
|
|
34
|
+
distance = city_list[1] * 2
|
|
35
|
+
iteration = 0
|
|
36
|
+
while (count < recursive_seeding):
|
|
37
|
+
if (verbose == True):
|
|
38
|
+
print('Iteration = ', iteration, 'Distance = ', round(city_list[1], 2))
|
|
39
|
+
best_route = copy.deepcopy(city_list)
|
|
40
|
+
seed = copy.deepcopy(city_list)
|
|
41
|
+
for i in range(0, len(city_list[0]) - 2):
|
|
42
|
+
for j in range(i + 1, len(city_list[0]) - 1):
|
|
43
|
+
best_route[0][i:j + 1] = list(reversed(best_route[0][i:j + 1]))
|
|
44
|
+
best_route[0][-1] = best_route[0][0]
|
|
45
|
+
best_route[1] = distance_calc(distance_matrix, best_route)
|
|
46
|
+
if (city_list[1] > best_route[1]):
|
|
47
|
+
city_list = copy.deepcopy(best_route)
|
|
48
|
+
best_route = copy.deepcopy(seed)
|
|
49
|
+
count = count + 1
|
|
50
|
+
iteration = iteration + 1
|
|
51
|
+
if (distance > city_list[1] and recursive_seeding < 0):
|
|
52
|
+
distance = city_list[1]
|
|
53
|
+
count = -2
|
|
54
|
+
recursive_seeding = -1
|
|
55
|
+
elif (city_list[1] >= distance and recursive_seeding < 0):
|
|
56
|
+
count = -1
|
|
57
|
+
recursive_seeding = -2
|
|
58
|
+
return city_list[0], city_list[1]
|
|
59
|
+
|
|
60
|
+
############################################################################
|
|
61
|
+
|
|
62
|
+
# Function: Cheapest insertion Position
|
|
63
|
+
def best_insertion(distance_matrix, temp):
|
|
64
|
+
if len(temp) <= 2:
|
|
65
|
+
return temp
|
|
66
|
+
new_node = temp[-1]
|
|
67
|
+
base = temp[:-1]
|
|
68
|
+
base_closed = base + [base[0]]
|
|
69
|
+
best_pos = None
|
|
70
|
+
best_delta = float('+inf')
|
|
71
|
+
for i in range(0, len(base)):
|
|
72
|
+
a = base_closed[i]
|
|
73
|
+
b = base_closed[i + 1]
|
|
74
|
+
delta = (distance_matrix[a, new_node] + distance_matrix[new_node, b] - distance_matrix[a, b])
|
|
75
|
+
if delta < best_delta:
|
|
76
|
+
best_delta = delta
|
|
77
|
+
best_pos = i + 1
|
|
78
|
+
out = base[:]
|
|
79
|
+
out.insert(best_pos, new_node)
|
|
80
|
+
return out
|
|
81
|
+
|
|
82
|
+
############################################################################
|
|
83
|
+
|
|
84
|
+
# Function: Farthest Insertion
|
|
85
|
+
def farthest_insertion(distance_matrix, local_search = True, verbose = True):
|
|
86
|
+
best_val = float('+inf')
|
|
87
|
+
best_route = []
|
|
88
|
+
n = distance_matrix.shape[0]
|
|
89
|
+
initial_location = -1
|
|
90
|
+
for i1 in range(0, n):
|
|
91
|
+
if (initial_location != -1):
|
|
92
|
+
i1 = initial_location - 1
|
|
93
|
+
dist = np.copy(distance_matrix).astype(float)
|
|
94
|
+
np.fill_diagonal(dist, float('-inf'))
|
|
95
|
+
idx = dist[i1, :].argmax()
|
|
96
|
+
temp = [i1, idx]
|
|
97
|
+
in_tour = set(temp)
|
|
98
|
+
for _ in range(0, n - 2):
|
|
99
|
+
remaining = [u for u in range(0, n) if u not in in_tour]
|
|
100
|
+
best_u = None
|
|
101
|
+
best_score = float('-inf')
|
|
102
|
+
for u in remaining:
|
|
103
|
+
score = min(distance_matrix[u, t] for t in temp)
|
|
104
|
+
if score > best_score:
|
|
105
|
+
best_score = score
|
|
106
|
+
best_u = u
|
|
107
|
+
temp.append(best_u)
|
|
108
|
+
in_tour.add(best_u)
|
|
109
|
+
temp = best_insertion(distance_matrix, temp)
|
|
110
|
+
route = temp + [temp[0]]
|
|
111
|
+
route = [x + 1 for x in route]
|
|
112
|
+
val = distance_calc(distance_matrix, [route, 1])
|
|
113
|
+
if local_search:
|
|
114
|
+
seed = [route, val]
|
|
115
|
+
route2, val2 = local_search_2_opt(distance_matrix, seed, -1, True)
|
|
116
|
+
route, val = route2, val2
|
|
117
|
+
if (val < best_val):
|
|
118
|
+
best_val = val
|
|
119
|
+
best_route = [item for item in route]
|
|
120
|
+
if (verbose == True):
|
|
121
|
+
print('Iteration = ', i1 + 1, 'Distance = ', round(best_val, 2))
|
|
122
|
+
if (initial_location != -1):
|
|
123
|
+
break
|
|
124
|
+
return best_route, best_val
|
|
125
|
+
|
|
126
|
+
############################################################################
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
|
|
3
|
+
# Created by: Prof. Valdecy Pereira, D.Sc.
|
|
4
|
+
# UFF - Universidade Federal Fluminense (Brazil)
|
|
5
|
+
# email: valdecy.pereira@gmail.com
|
|
6
|
+
# Lesson: Or-Opt
|
|
7
|
+
#
|
|
8
|
+
# GitHub Repository: <https://github.com/Valdecy>
|
|
9
|
+
|
|
10
|
+
############################################################################
|
|
11
|
+
|
|
12
|
+
# Required Libraries
|
|
13
|
+
import copy
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
############################################################################
|
|
17
|
+
|
|
18
|
+
# Function: Tour Distance
|
|
19
|
+
def distance_calc(distance_matrix, city_tour):
|
|
20
|
+
distance = 0
|
|
21
|
+
for k in range(0, len(city_tour[0])-1):
|
|
22
|
+
m = k + 1
|
|
23
|
+
distance = distance + distance_matrix[city_tour[0][k]-1, city_tour[0][m]-1]
|
|
24
|
+
return distance
|
|
25
|
+
|
|
26
|
+
# Function: 2_opt
|
|
27
|
+
def local_search_2_opt(distance_matrix, city_tour, recursive_seeding = -1, verbose = True):
|
|
28
|
+
if (recursive_seeding < 0):
|
|
29
|
+
count = -2
|
|
30
|
+
else:
|
|
31
|
+
count = 0
|
|
32
|
+
city_list = copy.deepcopy(city_tour)
|
|
33
|
+
distance = city_list[1]*2
|
|
34
|
+
iteration = 0
|
|
35
|
+
while (count < recursive_seeding):
|
|
36
|
+
if (verbose == True):
|
|
37
|
+
print('Iteration = ', iteration, 'Distance = ', round(city_list[1], 2))
|
|
38
|
+
best_route = copy.deepcopy(city_list)
|
|
39
|
+
seed = copy.deepcopy(city_list)
|
|
40
|
+
for i in range(0, len(city_list[0]) - 2):
|
|
41
|
+
for j in range(i+1, len(city_list[0]) - 1):
|
|
42
|
+
best_route[0][i:j+1] = list(reversed(best_route[0][i:j+1]))
|
|
43
|
+
best_route[0][-1] = best_route[0][0]
|
|
44
|
+
best_route[1] = distance_calc(distance_matrix, best_route)
|
|
45
|
+
if (city_list[1] > best_route[1]):
|
|
46
|
+
city_list = copy.deepcopy(best_route)
|
|
47
|
+
best_route = copy.deepcopy(seed)
|
|
48
|
+
count = count + 1
|
|
49
|
+
iteration = iteration + 1
|
|
50
|
+
if (distance > city_list[1] and recursive_seeding < 0):
|
|
51
|
+
distance = city_list[1]
|
|
52
|
+
count = -2
|
|
53
|
+
recursive_seeding = -1
|
|
54
|
+
elif(city_list[1] >= distance and recursive_seeding < 0):
|
|
55
|
+
count = -1
|
|
56
|
+
recursive_seeding = -2
|
|
57
|
+
return city_list[0], city_list[1]
|
|
58
|
+
|
|
59
|
+
############################################################################
|
|
60
|
+
|
|
61
|
+
# Function: Insertion
|
|
62
|
+
def insertion(city_tour, distance_matrix, chain_length = 5, k_min = 1, k_max = 10, lambda_val = 0.1, vf_mode = True):
|
|
63
|
+
|
|
64
|
+
# -------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
def total_distance(dist_mat, route_idxs):
|
|
67
|
+
total = 0.0
|
|
68
|
+
for idx in range(1, len(route_idxs)):
|
|
69
|
+
total = total + dist_mat[route_idxs[idx - 1]][route_idxs[idx]]
|
|
70
|
+
return total
|
|
71
|
+
|
|
72
|
+
# -------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
route = copy.deepcopy(city_tour[0])
|
|
75
|
+
if route[0] != route[-1]:
|
|
76
|
+
route = route + [route[0]]
|
|
77
|
+
current_distance = total_distance(distance_matrix, route)
|
|
78
|
+
n = len(route) - 1
|
|
79
|
+
if isinstance(distance_matrix, np.ndarray):
|
|
80
|
+
if distance_matrix.ndim != 2 or distance_matrix.shape[0] != distance_matrix.shape[1]:
|
|
81
|
+
raise ValueError("distance_matrix must be a square 2D array.")
|
|
82
|
+
if n <= 1:
|
|
83
|
+
avg_graph_cost = 1.0
|
|
84
|
+
else:
|
|
85
|
+
avg_graph_cost = distance_matrix[np.triu_indices(n, k=1)].mean()
|
|
86
|
+
if not np.isfinite(avg_graph_cost) or avg_graph_cost <= 0:
|
|
87
|
+
avg_graph_cost = 1.0
|
|
88
|
+
else:
|
|
89
|
+
if n <= 1:
|
|
90
|
+
avg_graph_cost = 1.0
|
|
91
|
+
else:
|
|
92
|
+
s = 0.0
|
|
93
|
+
cnt = 0
|
|
94
|
+
for a in range(n):
|
|
95
|
+
for b in range(a + 1, n):
|
|
96
|
+
s = s + distance_matrix[a][b]
|
|
97
|
+
cnt = cnt + 1
|
|
98
|
+
avg_graph_cost = s / cnt if cnt else 1.0
|
|
99
|
+
|
|
100
|
+
k_sequence = list(range(int(k_max), int(k_min) - 1, -1))
|
|
101
|
+
max_passes = max(1, int(chain_length))
|
|
102
|
+
for _pass in range(0, max_passes):
|
|
103
|
+
improved_any = False
|
|
104
|
+
if vf_mode:
|
|
105
|
+
for i in range(1, len(route) - 1):
|
|
106
|
+
for k in k_sequence:
|
|
107
|
+
if i + k >= len(route):
|
|
108
|
+
continue
|
|
109
|
+
i1, i2 = route[i - 1], route[i]
|
|
110
|
+
i3, i4 = route[i + k - 1], route[i + k]
|
|
111
|
+
g = (distance_matrix[i1][i2] +
|
|
112
|
+
distance_matrix[i3][i4] -
|
|
113
|
+
distance_matrix[i1][i4])
|
|
114
|
+
d_bar = current_distance / max(1, n)
|
|
115
|
+
l_bar = (2.0 * avg_graph_cost) - d_bar
|
|
116
|
+
if g <= lambda_val * l_bar:
|
|
117
|
+
continue
|
|
118
|
+
segment = route[i:i + k]
|
|
119
|
+
rem = route[:i] + route[i + k:]
|
|
120
|
+
moved = False
|
|
121
|
+
for j in range(1, len(rem)):
|
|
122
|
+
temp = rem[:j] + segment + rem[j:]
|
|
123
|
+
cost = total_distance(distance_matrix, temp)
|
|
124
|
+
if cost + 1e-12 < current_distance:
|
|
125
|
+
route = temp
|
|
126
|
+
current_distance = cost
|
|
127
|
+
improved_any = True
|
|
128
|
+
moved = True
|
|
129
|
+
break
|
|
130
|
+
if moved:
|
|
131
|
+
break
|
|
132
|
+
else:
|
|
133
|
+
for k in k_sequence:
|
|
134
|
+
improved_k = True
|
|
135
|
+
while improved_k:
|
|
136
|
+
improved_k = False
|
|
137
|
+
last_idx = len(route) - 1
|
|
138
|
+
for i in range(1, last_idx):
|
|
139
|
+
if i + k > last_idx:
|
|
140
|
+
continue
|
|
141
|
+
i1, i2 = route[i - 1], route[i]
|
|
142
|
+
i3, i4 = route[i + k - 1], route[i + k]
|
|
143
|
+
g = (distance_matrix[i1][i2] +
|
|
144
|
+
distance_matrix[i3][i4] -
|
|
145
|
+
distance_matrix[i1][i4])
|
|
146
|
+
d_bar = current_distance / max(1, n)
|
|
147
|
+
l_bar = (2.0 * avg_graph_cost) - d_bar
|
|
148
|
+
if g <= lambda_val * l_bar:
|
|
149
|
+
continue
|
|
150
|
+
segment = route[i:i + k]
|
|
151
|
+
rem = route[:i] + route[i + k:]
|
|
152
|
+
for j in range(1, len(rem)):
|
|
153
|
+
temp = rem[:j] + segment + rem[j:]
|
|
154
|
+
cost = total_distance(distance_matrix, temp)
|
|
155
|
+
if cost + 1e-12 < current_distance:
|
|
156
|
+
route = temp
|
|
157
|
+
current_distance = cost
|
|
158
|
+
improved_k = True
|
|
159
|
+
improved_any = True
|
|
160
|
+
break
|
|
161
|
+
if improved_k:
|
|
162
|
+
break
|
|
163
|
+
if not improved_any:
|
|
164
|
+
break
|
|
165
|
+
return route, current_distance
|
|
166
|
+
|
|
167
|
+
# Function: Or - Opt
|
|
168
|
+
def local_search_or_opt(city_tour, distance_matrix, iterations = 100, chain_length = 3, k1 = 1, k2 = 5, lbd = 0.1, vf = True, local_search = True, verbose = True):
|
|
169
|
+
route = city_tour[0]
|
|
170
|
+
distance = city_tour[1]
|
|
171
|
+
route = [item - 1 for item in route]
|
|
172
|
+
no_improve = 0
|
|
173
|
+
for i in range(0, iterations):
|
|
174
|
+
new_route, new_distance = insertion([route, distance], distance_matrix, chain_length, k1, k2, lbd, vf)
|
|
175
|
+
if new_distance < distance:
|
|
176
|
+
route = copy.deepcopy(new_route)
|
|
177
|
+
distance = new_distance
|
|
178
|
+
no_improve = 0
|
|
179
|
+
else:
|
|
180
|
+
no_improve = no_improve + 1
|
|
181
|
+
if no_improve >= 2:
|
|
182
|
+
break
|
|
183
|
+
if (verbose == True):
|
|
184
|
+
print('Iteration = ', i, 'Distance = ', round(distance, 2))
|
|
185
|
+
route = [item + 1 for item in route]
|
|
186
|
+
if (local_search == True):
|
|
187
|
+
print('')
|
|
188
|
+
print('Local Search:')
|
|
189
|
+
route, distance = local_search_2_opt(distance_matrix, [route, distance], recursive_seeding = -1, verbose = verbose)
|
|
190
|
+
return route, distance
|
|
191
|
+
|
|
192
|
+
############################################################################
|
|
@@ -97,8 +97,7 @@ def ruin_and_recreate(city_tour, distance_matrix, iterations = 100, ruin_rate =
|
|
|
97
97
|
distance = city_list[1]
|
|
98
98
|
best_r = copy.deepcopy(route)
|
|
99
99
|
best_d = distance
|
|
100
|
-
|
|
101
|
-
for _ in range(0, iterations):
|
|
100
|
+
for i in range(0, iterations):
|
|
102
101
|
n_remove = max(1, int(len(removable) * ruin_rate))
|
|
103
102
|
to_remove = set(np.random.choice(removable, min(n_remove, len(removable)), replace = False))
|
|
104
103
|
route = [city for city in route if city not in to_remove]
|
|
@@ -107,11 +106,10 @@ def ruin_and_recreate(city_tour, distance_matrix, iterations = 100, ruin_rate =
|
|
|
107
106
|
route = regret2_insertion(route, to_remove, distance_matrix)
|
|
108
107
|
distance = distance_calc(distance_matrix, [route, distance])
|
|
109
108
|
if (verbose == True):
|
|
110
|
-
print('Iteration = ',
|
|
109
|
+
print('Iteration = ', i, 'Distance = ', round(best_d, 2))
|
|
111
110
|
if distance < best_d:
|
|
112
111
|
best_r = copy.deepcopy(route)
|
|
113
112
|
best_d = distance
|
|
114
|
-
iteration = iteration + 1
|
|
115
113
|
if (local_search == True):
|
|
116
114
|
print('')
|
|
117
115
|
print('Local Search:')
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
############################################################################
|
|
2
|
+
|
|
3
|
+
# Created by: Prof. Valdecy Pereira, D.Sc.
|
|
4
|
+
# UFF - Universidade Federal Fluminense (Brazil)
|
|
5
|
+
# email: valdecy.pereira@gmail.com
|
|
6
|
+
# Lesson: pyCombinatorial - SSI (Spectral Seriation Initializer)
|
|
7
|
+
|
|
8
|
+
# GitHub Repository: <https://github.com/Valdecy>
|
|
9
|
+
|
|
10
|
+
############################################################################
|
|
11
|
+
|
|
12
|
+
# Required Libraries
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
from numba import njit
|
|
16
|
+
from scipy.sparse import coo_matrix, diags
|
|
17
|
+
from scipy.sparse.linalg import eigsh
|
|
18
|
+
|
|
19
|
+
############################################################################
|
|
20
|
+
|
|
21
|
+
# Function: Tour Lenght
|
|
22
|
+
@njit(fastmath = True)
|
|
23
|
+
def tour_length(tour, D):
|
|
24
|
+
n = len(tour)
|
|
25
|
+
L = 0.0
|
|
26
|
+
for i in range(n):
|
|
27
|
+
L = L + D[tour[i], tour[(i + 1) % n]]
|
|
28
|
+
return L
|
|
29
|
+
|
|
30
|
+
# Function: 2-opt Passes
|
|
31
|
+
@njit(fastmath = True)
|
|
32
|
+
def two_opt(tour, D, max_passes):
|
|
33
|
+
n = len(tour)
|
|
34
|
+
improved = True
|
|
35
|
+
passes = 0
|
|
36
|
+
while improved and passes < max_passes:
|
|
37
|
+
improved = False
|
|
38
|
+
passes = passes + 1
|
|
39
|
+
for i in range(0, n - 1):
|
|
40
|
+
a_idx = tour[i]
|
|
41
|
+
b_idx = tour[(i + 1)]
|
|
42
|
+
k_end = n - 1 if i == 0 else n
|
|
43
|
+
for k in range(i + 2, k_end):
|
|
44
|
+
c_idx = tour[k]
|
|
45
|
+
d_idx = tour[(k + 1) % n]
|
|
46
|
+
current_cost = D[a_idx, b_idx] + D[c_idx, d_idx]
|
|
47
|
+
new_cost = D[a_idx, c_idx] + D[b_idx, d_idx]
|
|
48
|
+
if new_cost < current_cost:
|
|
49
|
+
p1 = i + 1
|
|
50
|
+
p2 = k
|
|
51
|
+
while p1 < p2:
|
|
52
|
+
temp = tour[p1]
|
|
53
|
+
tour[p1] = tour[p2]
|
|
54
|
+
tour[p2] = temp
|
|
55
|
+
p1 = p1 + 1
|
|
56
|
+
p2 = p2 - 1
|
|
57
|
+
improved = True
|
|
58
|
+
return tour
|
|
59
|
+
|
|
60
|
+
############################################################################
|
|
61
|
+
|
|
62
|
+
# Function: KNN
|
|
63
|
+
def knn_indices(D, k):
|
|
64
|
+
return np.argsort(D, axis=1)[:, 1 : k + 1]
|
|
65
|
+
|
|
66
|
+
# Function: Affinity
|
|
67
|
+
def build_affinity_sparse(D, k, sigma_mode, sigma_fixed):
|
|
68
|
+
n = D.shape[0]
|
|
69
|
+
nbrs = knn_indices(D.astype(float), k)
|
|
70
|
+
if sigma_mode == 'adaptive':
|
|
71
|
+
sig = D[np.arange(n), nbrs[:, -1]].astype(float) + 1e-12
|
|
72
|
+
else:
|
|
73
|
+
sig = np.full(n, sigma_fixed, dtype = float)
|
|
74
|
+
rows, cols, vals = [], [], []
|
|
75
|
+
for i in range(n):
|
|
76
|
+
si = sig[i]
|
|
77
|
+
for j in nbrs[i]:
|
|
78
|
+
sj = sig[j]
|
|
79
|
+
dij = float(D[i, j])
|
|
80
|
+
if sigma_mode == 'adaptive':
|
|
81
|
+
w = np.exp(-(dij * dij) / (si * sj + 1e-12))
|
|
82
|
+
else:
|
|
83
|
+
w = np.exp(-(dij * dij) / (2.0 * sigma_fixed * sigma_fixed + 1e-12))
|
|
84
|
+
rows.append(i); cols.append(j); vals.append(w)
|
|
85
|
+
W = coo_matrix((vals, (rows, cols)), shape = (n, n)).tocsr()
|
|
86
|
+
W = (W + W.T).tocsr()
|
|
87
|
+
W = W.tolil()
|
|
88
|
+
W.setdiag(0.0)
|
|
89
|
+
W = W.tocsr()
|
|
90
|
+
W.eliminate_zeros()
|
|
91
|
+
return W
|
|
92
|
+
|
|
93
|
+
# Function: Laplacian
|
|
94
|
+
def laplacian_from_W(W):
|
|
95
|
+
d = np.array(W.sum(axis = 1)).reshape(-1)
|
|
96
|
+
return (diags(d) - W).tocsr()
|
|
97
|
+
|
|
98
|
+
# Function: Fiedler Vector
|
|
99
|
+
def fiedler_vector(L):
|
|
100
|
+
vals, vecs = eigsh(L, k = 4, which = 'SM')
|
|
101
|
+
idx = np.argsort(vals)
|
|
102
|
+
vals = vals[idx]
|
|
103
|
+
vecs = vecs[:, idx]
|
|
104
|
+
eps = 1e-10
|
|
105
|
+
j = 0
|
|
106
|
+
while j < len(vals) and vals[j] < eps:
|
|
107
|
+
j = j + 1
|
|
108
|
+
if j == 0 and len(vals) > 1:
|
|
109
|
+
j = 1
|
|
110
|
+
j = min(j, len(vals) - 1)
|
|
111
|
+
return vecs[:, j].copy()
|
|
112
|
+
|
|
113
|
+
############################################################################
|
|
114
|
+
|
|
115
|
+
# Spectral Seriation Initializer
|
|
116
|
+
def spectral_seriation_initializer(D, k = 12, iterations = 800, sigma_noise = 0.003, sigma_mode = 'adaptive', sigma_fixed = 250.0, two_opt_passes = 10, rnd = 7, verbose = True):
|
|
117
|
+
D = np.asarray(D)
|
|
118
|
+
n = D.shape[0]
|
|
119
|
+
rng = np.random.default_rng(rnd)
|
|
120
|
+
W = build_affinity_sparse(D, k, sigma_mode, sigma_fixed)
|
|
121
|
+
L = laplacian_from_W(W)
|
|
122
|
+
x = fiedler_vector(L)
|
|
123
|
+
best_L = float("inf")
|
|
124
|
+
best_tour = None
|
|
125
|
+
for nt in range(0, iterations):
|
|
126
|
+
x_noisy = x + rng.normal(0.0, sigma_noise, size = n)
|
|
127
|
+
tour = np.argsort(x_noisy).astype(np.int32)
|
|
128
|
+
if rng.random() < 0.5:
|
|
129
|
+
tour = tour[::-1]
|
|
130
|
+
shift = int(rng.integers(0, n))
|
|
131
|
+
tour = np.roll(tour, shift)
|
|
132
|
+
tour = two_opt(tour, D, two_opt_passes)
|
|
133
|
+
Lc = tour_length(tour, D)
|
|
134
|
+
if Lc < best_L:
|
|
135
|
+
best_L = Lc
|
|
136
|
+
best_tour = tour
|
|
137
|
+
if verbose:
|
|
138
|
+
print('Iteration = ', nt, 'Distance = ', best_L)
|
|
139
|
+
best_tour = [item + 1 for item in best_tour]
|
|
140
|
+
best_tour.append(best_tour[0])
|
|
141
|
+
return best_tour, int(best_L)
|
|
142
|
+
|
|
143
|
+
############################################################################
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pycombinatorial
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: A library to solve TSP (Travelling Salesman Problem) using Exact Algorithms, Heuristics, Metaheuristics and Reinforcement Learning
|
|
5
5
|
Home-page: https://github.com/Valdecy/pyCombinatorial
|
|
6
6
|
Author: Valdecy Pereira
|
|
@@ -15,7 +15,7 @@ License-File: LICENSE
|
|
|
15
15
|
|
|
16
16
|
**pyCombinatorial** is a Python-based library designed to tackle the classic Travelling Salesman Problem (TSP) through a diverse set of **Exact Algorithms**, **Heuristics**, **Metaheuristics** and **Reinforcement Learning**. It brings together both well-established and cutting-edge methodologies, offering end-users a flexible toolkit to generate high-quality solutions for TSP instances of various sizes and complexities.
|
|
17
17
|
|
|
18
|
-
Techniques: **2-opt**; **2.5-opt**; **3-opt**; **4-opt**; **5-opt**; **2-opt Stochastic**; **2.5-opt Stochastic**; **3-opt Stochastic**; **4-opt Stochastic**; **5-opt Stochastic**; **Ant Colony Optimization**; **Adaptive Large Neighborhood Search**; **Bellman-Held-Karp Exact Algorithm**; **Bitonic Tour**; **Branch & Bound**; **BRKGA** (Biased Random Key Genetic Algorithm); **Brute Force**; **Cheapest Insertion**; **Christofides Algorithm**; **Clarke & Wright** (Savings Heuristic); **Concave Hull Algorithm**; **Convex Hull Algorithm**; **Elastic Net**; **Extremal Optimization**; **Farthest Insertion**; **FRNN** (Fixed Radius Near Neighbor); **Genetic Algorithm**; **GRASP** (Greedy Randomized Adaptive Search Procedure); **Greedy Karp-Steele Patching**; **Guided Search**; **Hopfield Network**; **Iterated Search**; **Karp-Steele Patching**; **Large Neighborhood Search**; **Multifragment Heuristic**; **Nearest Insertion**; **Nearest Neighbour**; **Random Insertion**; **Random Tour**; **RL Q-Learning**; **RL Double Q-Learning**; **RL S.A.R.S.A** (State Action Reward State Action); **Ruin & Recreate**; **Scatter Search**; **Simulated Annealing**; **SOM** (Self Organizing Maps); **Space Filling Curve** (Hilbert); **Space Filling Curve** (Morton); **Space Filling Curve** (Sierpinski); **Stochastic Hill Climbing**; **Sweep**; **Tabu Search**; **Truncated Branch & Bound**; **Twice-Around the Tree Algorithm** (Double Tree Algorithm); **Variable Neighborhood Search**; **Zero Suffix Method**.
|
|
18
|
+
Techniques: **2-opt**; **2.5-opt**; **3-opt**; **4-opt**; **5-opt**; **Or-opt**; **2-opt Stochastic**; **2.5-opt Stochastic**; **3-opt Stochastic**; **4-opt Stochastic**; **5-opt Stochastic**; **Ant Colony Optimization**; **Adaptive Large Neighborhood Search**; **Bellman-Held-Karp Exact Algorithm**; **Bitonic Tour**; **Branch & Bound**; **BRKGA** (Biased Random Key Genetic Algorithm); **Brute Force**; **Cheapest Insertion**; **Christofides Algorithm**; **Clarke & Wright** (Savings Heuristic); **Concave Hull Algorithm**; **Convex Hull Algorithm**; **Elastic Net**; **Extremal Optimization**; **Farthest Insertion**; **FRNN** (Fixed Radius Near Neighbor); **Genetic Algorithm**; **GRASP** (Greedy Randomized Adaptive Search Procedure); **Greedy Karp-Steele Patching**; **Guided Search**; **Hopfield Network**; **Iterated Search**; **Karp-Steele Patching**; **Large Neighborhood Search**; **Multifragment Heuristic**; **Nearest Insertion**; **Nearest Neighbour**; **Random Insertion**; **Random Tour**; **RL Q-Learning**; **RL Double Q-Learning**; **RL S.A.R.S.A** (State Action Reward State Action); **Ruin & Recreate**; **Scatter Search**; **Simulated Annealing**; **SOM** (Self Organizing Maps); **Space Filling Curve** (Hilbert); **Space Filling Curve** (Morton); **Space Filling Curve** (Sierpinski); **Spectral Seriation Initializer**; **Stochastic Hill Climbing**; **Sweep**; **Tabu Search**; **Truncated Branch & Bound**; **Twice-Around the Tree Algorithm** (Double Tree Algorithm); **Variable Neighborhood Search**; **Zero Suffix Method**.
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
21
21
|
|
|
@@ -76,6 +76,7 @@ print('Total Distance: ', round(distance, 2))
|
|
|
76
76
|
- 3-opt ([ Colab Demo ](https://colab.research.google.com/drive/1iAZLawLBZ-7yaPCyobMtel1SvBamxtjL?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
77
77
|
- 4-opt ([ Colab Demo ](https://colab.research.google.com/drive/1N8HKhVY4s20sfqo8IWIaCY-NHVk6gARS?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
78
78
|
- 5-opt ([ Colab Demo ](https://colab.research.google.com/drive/15Qrk-7H4oRaTR77ADvwkiN0sLvycgFDH?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
79
|
+
- Or-opt ([ Colab Demo ](https://colab.research.google.com/drive/1p7JwrFLH83ZroCzIweXLCAXYgA3FKAI0?usp=sharing)) ( [ Paper ](https://doi.org/10.1057/palgrave.jors.2602160))
|
|
79
80
|
- 2-opt Stochastic ([ Colab Demo ](https://colab.research.google.com/drive/1xTm__7OwQVC_KX2b-eExLGgG1DgnJ10a?usp=sharing)) ( [ Paper ](https://doi.org/10.1016/j.trpro.2014.10.001))
|
|
80
81
|
- 2.5-opt Stochastic ([ Colab Demo ](https://colab.research.google.com/drive/16W_QqJ1PebVgqUx8NFOSS5kG3DsJ51UQ?usp=sharing)) ( [ Paper ](https://doi.org/10.1007/s10955-007-9382-1))
|
|
81
82
|
- 3-opt Stochastic ([ Colab Demo ](https://colab.research.google.com/drive/1A5lPW6BSDD2rLNDlnpQo44U8jwKcAGXL?usp=sharing)) ( [ Paper ](https://isd.ktu.lt/it2011//material/Proceedings/1_AI_5.pdf))
|
|
@@ -120,6 +121,7 @@ print('Total Distance: ', round(distance, 2))
|
|
|
120
121
|
- Space Filling Curve (Hilbert) ([ Colab Demo ](https://colab.research.google.com/drive/1FXzWrUBjdbJBngRFHv66CZw5pFN3yOs8?usp=sharing)) ( [ Paper ](https://doi.org/10.1016/0960-0779(95)80046-J))
|
|
121
122
|
- Space Filling Curve (Morton) ([ Colab Demo ](https://colab.research.google.com/drive/1Z13kXyi7eaNQbBUmhvwuQjY4VaUfGVbs?usp=sharing)) ( [ Paper ](https://dominoweb.draco.res.ibm.com/reports/Morton1966.pdf))
|
|
122
123
|
- Space Filling Curve (Sierpinski) ([ Colab Demo ](https://colab.research.google.com/drive/1w-Zptd5kOryCwvQ0qSNBNhPXC61c8QXF?usp=sharing)) ( [ Paper ](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.67.9061&rep=rep1&type=pdf))
|
|
124
|
+
- Spectral Seriation Initializer ([ Colab Demo ](https://colab.research.google.com/drive/1lG0pYxASU75qh0jK-A_eMCoPpCWv0I4V?usp=sharing)) ( [ Paper ](https://doi.org/10.1137/S009753979528577))
|
|
123
125
|
- Stochastic Hill Climbing ([ Colab Demo ](https://colab.research.google.com/drive/1_wP6vg4JoRHGItGxEtXcf9Y9OuuoDlDl?usp=sharing)) ( [ Paper ](http://aima.cs.berkeley.edu/))
|
|
124
126
|
- Sweep ([ Colab Demo ](https://colab.research.google.com/drive/1AkAn4yeomAp6POBslk3Asd6OrxfBrHT7?usp=sharing)) ( [ Paper ](http://dx.doi.org/10.1287/opre.22.2.340))
|
|
125
127
|
- Tabu Search ([ Colab Demo ](https://colab.research.google.com/drive/1SRwQrBaxkKk18SDvQPy--0yNRWdl6Y1G?usp=sharing)) ( [ Paper ](https://doi.org/10.1287/ijoc.1.3.190))
|
|
@@ -39,6 +39,7 @@ pyCombinatorial/algorithm/opt_4.py
|
|
|
39
39
|
pyCombinatorial/algorithm/opt_4s.py
|
|
40
40
|
pyCombinatorial/algorithm/opt_5.py
|
|
41
41
|
pyCombinatorial/algorithm/opt_5s.py
|
|
42
|
+
pyCombinatorial/algorithm/opt_or.py
|
|
42
43
|
pyCombinatorial/algorithm/rl_double_ql.py
|
|
43
44
|
pyCombinatorial/algorithm/rl_ql.py
|
|
44
45
|
pyCombinatorial/algorithm/rl_sarsa.py
|
|
@@ -55,6 +56,7 @@ pyCombinatorial/algorithm/som.py
|
|
|
55
56
|
pyCombinatorial/algorithm/spfc_h.py
|
|
56
57
|
pyCombinatorial/algorithm/spfc_m.py
|
|
57
58
|
pyCombinatorial/algorithm/spfc_s.py
|
|
59
|
+
pyCombinatorial/algorithm/ssi.py
|
|
58
60
|
pyCombinatorial/algorithm/swp.py
|
|
59
61
|
pyCombinatorial/algorithm/tat.py
|
|
60
62
|
pyCombinatorial/algorithm/tbb.py
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
############################################################################
|
|
2
|
-
|
|
3
|
-
# Created by: Prof. Valdecy Pereira, D.Sc.
|
|
4
|
-
# UFF - Universidade Federal Fluminense (Brazil)
|
|
5
|
-
# email: valdecy.pereira@gmail.com
|
|
6
|
-
# Lesson: Farthest Insertion
|
|
7
|
-
|
|
8
|
-
# GitHub Repository: <https://github.com/Valdecy>
|
|
9
|
-
|
|
10
|
-
############################################################################
|
|
11
|
-
|
|
12
|
-
# Required Libraries
|
|
13
|
-
import copy
|
|
14
|
-
import numpy as np
|
|
15
|
-
|
|
16
|
-
############################################################################
|
|
17
|
-
|
|
18
|
-
# Function: Tour Distance
|
|
19
|
-
def distance_calc(distance_matrix, city_tour):
|
|
20
|
-
distance = 0
|
|
21
|
-
for k in range(0, len(city_tour[0])-1):
|
|
22
|
-
m = k + 1
|
|
23
|
-
distance = distance + distance_matrix[city_tour[0][k]-1, city_tour[0][m]-1]
|
|
24
|
-
return distance
|
|
25
|
-
|
|
26
|
-
# Function: 2_opt
|
|
27
|
-
def local_search_2_opt(distance_matrix, city_tour, recursive_seeding = -1, verbose = True):
|
|
28
|
-
if (recursive_seeding < 0):
|
|
29
|
-
count = -2
|
|
30
|
-
else:
|
|
31
|
-
count = 0
|
|
32
|
-
city_list = copy.deepcopy(city_tour)
|
|
33
|
-
distance = city_list[1]*2
|
|
34
|
-
iteration = 0
|
|
35
|
-
while (count < recursive_seeding):
|
|
36
|
-
if (verbose == True):
|
|
37
|
-
print('Iteration = ', iteration, 'Distance = ', round(city_list[1], 2))
|
|
38
|
-
best_route = copy.deepcopy(city_list)
|
|
39
|
-
seed = copy.deepcopy(city_list)
|
|
40
|
-
for i in range(0, len(city_list[0]) - 2):
|
|
41
|
-
for j in range(i+1, len(city_list[0]) - 1):
|
|
42
|
-
best_route[0][i:j+1] = list(reversed(best_route[0][i:j+1]))
|
|
43
|
-
best_route[0][-1] = best_route[0][0]
|
|
44
|
-
best_route[1] = distance_calc(distance_matrix, best_route)
|
|
45
|
-
if (city_list[1] > best_route[1]):
|
|
46
|
-
city_list = copy.deepcopy(best_route)
|
|
47
|
-
best_route = copy.deepcopy(seed)
|
|
48
|
-
count = count + 1
|
|
49
|
-
iteration = iteration + 1
|
|
50
|
-
if (distance > city_list[1] and recursive_seeding < 0):
|
|
51
|
-
distance = city_list[1]
|
|
52
|
-
count = -2
|
|
53
|
-
recursive_seeding = -1
|
|
54
|
-
elif(city_list[1] >= distance and recursive_seeding < 0):
|
|
55
|
-
count = -1
|
|
56
|
-
recursive_seeding = -2
|
|
57
|
-
return city_list[0], city_list[1]
|
|
58
|
-
|
|
59
|
-
############################################################################
|
|
60
|
-
|
|
61
|
-
# Function: Best Insertion
|
|
62
|
-
def best_insertion(distance_matrix, temp):
|
|
63
|
-
temp_ = [item+1 for item in temp]
|
|
64
|
-
temp_ = temp_ + [temp_[0]]
|
|
65
|
-
d = distance_calc(distance_matrix, [temp_, 1])
|
|
66
|
-
seed = [temp_, d]
|
|
67
|
-
temp_, _ = local_search_2_opt(distance_matrix, seed, recursive_seeding = -1, verbose = False)
|
|
68
|
-
temp = [item-1 for item in temp_[:-1]]
|
|
69
|
-
return temp
|
|
70
|
-
|
|
71
|
-
############################################################################
|
|
72
|
-
|
|
73
|
-
# Function: Farthest Insertion
|
|
74
|
-
def farthest_insertion(distance_matrix, initial_location = -1, verbose = True):
|
|
75
|
-
maximum = float('+inf')
|
|
76
|
-
distance = float('+inf')
|
|
77
|
-
route = []
|
|
78
|
-
for i1 in range(0, distance_matrix.shape[0]):
|
|
79
|
-
if (initial_location != -1):
|
|
80
|
-
i1 = initial_location-1
|
|
81
|
-
temp = []
|
|
82
|
-
dist = np.copy(distance_matrix)
|
|
83
|
-
dist = dist.astype(float)
|
|
84
|
-
np.fill_diagonal(dist, float('-inf'))
|
|
85
|
-
idx = dist[i1,:].argmax()
|
|
86
|
-
dist[i1,:] = float('-inf')
|
|
87
|
-
dist[:,i1] = float('-inf')
|
|
88
|
-
temp.append(i1)
|
|
89
|
-
temp.append(idx)
|
|
90
|
-
for j in range(0, distance_matrix.shape[0]-2):
|
|
91
|
-
i2 = idx
|
|
92
|
-
idx = dist[i2,:].argmax()
|
|
93
|
-
dist[i2,:] = float('-inf')
|
|
94
|
-
dist[:,i2] = float('-inf')
|
|
95
|
-
temp.append(idx)
|
|
96
|
-
temp = best_insertion(distance_matrix, temp)
|
|
97
|
-
temp = temp + [temp[0]]
|
|
98
|
-
temp = [item + 1 for item in temp]
|
|
99
|
-
val = distance_calc(distance_matrix, [temp, 1])
|
|
100
|
-
if (val < maximum):
|
|
101
|
-
maximum = val
|
|
102
|
-
distance = val
|
|
103
|
-
route = [item for item in temp]
|
|
104
|
-
if (verbose == True):
|
|
105
|
-
print('Iteration = ', i1, 'Distance = ', round(distance, 2))
|
|
106
|
-
if (initial_location == -1):
|
|
107
|
-
continue
|
|
108
|
-
else:
|
|
109
|
-
break
|
|
110
|
-
return route, distance
|
|
111
|
-
|
|
112
|
-
############################################################################
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pycombinatorial-2.1.0 → pycombinatorial-2.1.2}/pycombinatorial.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|