ascent-science-aco 0.1.0__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.
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: ascent-science-aco
3
+ Version: 0.1.0
4
+ Summary: Ant colony optimizer plugin for Ascent Science SDK
5
+ License-Expression: LicenseRef-Proprietary
6
+ Requires-Python: >=3.9
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: ascent-science<0.10,>=0.9
9
+
10
+ # Ascent Science ACO plugin
11
+
12
+ Install with `python -m pip install ascent-science-aco`, then use
13
+ `ascent.combinatorial.ACOOptimizer(...)`.
@@ -0,0 +1,4 @@
1
+ # Ascent Science ACO plugin
2
+
3
+ Install with `python -m pip install ascent-science-aco`, then use
4
+ `ascent.combinatorial.ACOOptimizer(...)`.
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ascent-science-aco"
7
+ version = "0.1.0"
8
+ description = "Ant colony optimizer plugin for Ascent Science SDK"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "LicenseRef-Proprietary"
12
+ dependencies = ["ascent-science>=0.9,<0.10"]
13
+
14
+ [project.entry-points."ascent.algorithms"]
15
+ aco = "ascent_science_aco:register"
16
+
17
+ [tool.setuptools.packages.find]
18
+ where = ["src"]
19
+ include = ["ascent_science_aco*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,163 @@
1
+ """Ant colony optimization plugin for Ascent Science."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ import random
7
+ import time
8
+ from dataclasses import asdict, dataclass
9
+
10
+ from ascent.core import AlgorithmSpec
11
+ from ascent.plugin_api import register_algorithm, square_matrix, tracked, validated_license
12
+
13
+ __version__ = "0.1.0"
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class ACOResult:
18
+ best_route: list[int]
19
+ best_distance: float
20
+ history: list[float]
21
+ iterations: int
22
+ runtime_seconds: float
23
+ solver_name: str = "ACOOptimizer"
24
+ solver_version: str = "ascent-science-aco 0.1.0"
25
+ execution_location: str = "local"
26
+ status: str = "FEASIBLE"
27
+
28
+ @property
29
+ def best_solution(self):
30
+ return self.best_route
31
+
32
+ @property
33
+ def best_value(self):
34
+ return self.best_distance
35
+
36
+ @property
37
+ def result(self):
38
+ return self.best_route
39
+
40
+ def to_dict(self):
41
+ return asdict(self)
42
+
43
+
44
+ class ACOOptimizer:
45
+ def __init__(
46
+ self,
47
+ *,
48
+ user_id=None,
49
+ sdk_code=None,
50
+ endpoint=None,
51
+ license_path=None,
52
+ timeout=30.0,
53
+ report_usage=True,
54
+ validation_mode="cached",
55
+ ants=30,
56
+ evaporation=0.5,
57
+ alpha=1.0,
58
+ beta=2.0,
59
+ deposit=100.0,
60
+ iterations=100,
61
+ seed=None,
62
+ ):
63
+ self.endpoint, self.license_user_id, self._usage_reporter = validated_license(
64
+ endpoint, user_id, sdk_code, license_path, timeout, report_usage, validation_mode
65
+ )
66
+ self.ants = int(ants)
67
+ self.evaporation = float(evaporation)
68
+ self.alpha = float(alpha)
69
+ self.beta = float(beta)
70
+ self.deposit = float(deposit)
71
+ self.iterations = int(iterations)
72
+ self.seed = seed
73
+ if self.ants < 1 or self.iterations < 1:
74
+ raise ValueError("ants and iterations must be positive integers")
75
+ if not 0.0 < self.evaporation < 1.0:
76
+ raise ValueError("evaporation must be between 0 and 1")
77
+ if self.alpha < 0 or self.beta < 0 or self.deposit <= 0:
78
+ raise ValueError("alpha and beta cannot be negative; deposit must be positive")
79
+
80
+ def solve(self, distance_matrix, *, start_city=0, iterations=None, seed=None):
81
+ return self.solve_tsp(
82
+ distance_matrix, start_city=start_city, iterations=iterations, seed=seed
83
+ )
84
+
85
+ @tracked("aco")
86
+ def solve_tsp(self, distance_matrix, *, start_city=0, iterations=None, seed=None):
87
+ matrix = square_matrix(distance_matrix, "distance_matrix")
88
+ size = len(matrix)
89
+ count = self.iterations if iterations is None else int(iterations)
90
+ if size < 2:
91
+ raise ValueError("distance_matrix must contain at least two cities")
92
+ if any(value < 0 for row in matrix for value in row):
93
+ raise ValueError("distance_matrix entries cannot be negative")
94
+ if not isinstance(start_city, int) or not 0 <= start_city < size:
95
+ raise ValueError("start_city must be a valid city index")
96
+ if count < 1:
97
+ raise ValueError("iterations must be a positive integer")
98
+
99
+ started = time.perf_counter()
100
+ rng = random.Random(self.seed if seed is None else seed)
101
+ pheromone = [[1.0] * size for _ in range(size)]
102
+ best_route, best_distance = None, math.inf
103
+ history = []
104
+
105
+ def construct_tour():
106
+ route = [start_city]
107
+ unvisited = set(range(size))
108
+ unvisited.remove(start_city)
109
+ while unvisited:
110
+ current = route[-1]
111
+ candidates = sorted(unvisited)
112
+ weights = [
113
+ pheromone[current][city] ** self.alpha
114
+ * (1.0 / max(matrix[current][city], 1e-12)) ** self.beta
115
+ for city in candidates
116
+ ]
117
+ destination = rng.choices(candidates, weights=weights)[0]
118
+ route.append(destination)
119
+ unvisited.remove(destination)
120
+ route.append(start_city)
121
+ distance = sum(matrix[route[index]][route[index + 1]] for index in range(size))
122
+ return route, distance
123
+
124
+ for _ in range(count):
125
+ tours = [construct_tour() for _ in range(self.ants)]
126
+ for route, distance in tours:
127
+ if distance < best_distance:
128
+ best_route, best_distance = list(route), distance
129
+ for source in range(size):
130
+ for destination in range(size):
131
+ pheromone[source][destination] *= 1.0 - self.evaporation
132
+ for route, distance in tours:
133
+ reward = self.deposit / max(distance, 1e-12)
134
+ for index in range(size):
135
+ pheromone[route[index]][route[index + 1]] += reward
136
+ history.append(float(best_distance))
137
+
138
+ return ACOResult(
139
+ best_route=best_route or [start_city, start_city],
140
+ best_distance=float(best_distance),
141
+ history=history,
142
+ iterations=count,
143
+ runtime_seconds=time.perf_counter() - started,
144
+ )
145
+
146
+
147
+ def register():
148
+ return register_algorithm(
149
+ category="combinatorial",
150
+ exports={"ACOOptimizer": ACOOptimizer, "ACOResult": ACOResult},
151
+ specification=AlgorithmSpec(
152
+ name="aco",
153
+ family="metaheuristic",
154
+ problem_types=("tsp",),
155
+ input_kind="distance_matrix",
156
+ output_kind="route",
157
+ factory=ACOOptimizer,
158
+ description="Ant colony optimization for travelling-salesperson problems.",
159
+ ),
160
+ )
161
+
162
+
163
+ __all__ = ["ACOOptimizer", "ACOResult", "register"]
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: ascent-science-aco
3
+ Version: 0.1.0
4
+ Summary: Ant colony optimizer plugin for Ascent Science SDK
5
+ License-Expression: LicenseRef-Proprietary
6
+ Requires-Python: >=3.9
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: ascent-science<0.10,>=0.9
9
+
10
+ # Ascent Science ACO plugin
11
+
12
+ Install with `python -m pip install ascent-science-aco`, then use
13
+ `ascent.combinatorial.ACOOptimizer(...)`.
@@ -0,0 +1,9 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/ascent_science_aco/__init__.py
4
+ src/ascent_science_aco.egg-info/PKG-INFO
5
+ src/ascent_science_aco.egg-info/SOURCES.txt
6
+ src/ascent_science_aco.egg-info/dependency_links.txt
7
+ src/ascent_science_aco.egg-info/entry_points.txt
8
+ src/ascent_science_aco.egg-info/requires.txt
9
+ src/ascent_science_aco.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [ascent.algorithms]
2
+ aco = ascent_science_aco:register
@@ -0,0 +1 @@
1
+ ascent-science<0.10,>=0.9
@@ -0,0 +1 @@
1
+ ascent_science_aco