Skip to content

Commit 2351597

Browse files
committed
New Example: Facility Location
1 parent b6c72de commit 2351597

File tree

23 files changed

+625
-3
lines changed

23 files changed

+625
-3
lines changed

examples/object_oriented/boiler_plate_code/scripts/solve_task.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
migration_rate=0.00001, migration_frequency=1, termination_strategy=termination_strategy)"""
3535
"""agent = LateAcceptance(late_acceptance_size=10, tabu_entity_rate=0.0,
3636
mutation_rate_multiplier=None, move_probas=[0.5, 0.5, 0, 0, 0, 0],
37-
compare_to_global_frequency=1, termination_strategy=termination_strategy)"""
37+
compare_to_global_frequency=1000, termination_strategy=termination_strategy)"""
3838
"""agent = SimulatedAnnealing(initial_temperature=[1.0], cooling_rate=0.9999, tabu_entity_rate=0.0,
3939
mutation_rate_multiplier=None, move_probas=[0.5, 0.5, 0, 0, 0, 0],
4040
migration_frequency=10, compare_to_global_frequency=10, termination_strategy=termination_strategy)"""

examples/object_oriented/facility_location/__init__.py

Whitespace-only changes.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
3+
4+
5+
class CotConsumer:
6+
7+
def __init__(self, customer_id, location_id, demand, facility_id):
8+
self.customer_id = customer_id
9+
self.location_id = location_id
10+
self.demand = demand
11+
self.facility_id = facility_id
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
3+
4+
5+
class CotFacility:
6+
7+
def __init__(self, facility_id, location_id, setup_cost, capacity):
8+
self.facility_id = facility_id
9+
self.location_id = location_id
10+
self.setup_cost = setup_cost
11+
self.capacity = capacity
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
3+
from greyjack.cotwin.CotwinBase import CotwinBase
4+
5+
class Cotwin(CotwinBase):
6+
def __init__(self):
7+
super().__init__()

examples/object_oriented/facility_location/cotwin/__init__.py

Whitespace-only changes.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
class Consumer:
2+
"""
3+
Consumer has a demand that can be satisfied by any Facility with sufficient capacity.
4+
5+
Closer facilities are preferred as the distance affects travel time, signal quality, etc.
6+
This requirement is expressed by the distance from facility constraint.
7+
8+
One of the FLP's goals is to minimize total set-up cost by selecting cheaper facilities.
9+
This requirement is expressed by the setup cost constraint.
10+
"""
11+
12+
def __init__(self, id=None, location=None, demand=None):
13+
self.id = id
14+
self.location = location
15+
self.demand = demand
16+
self.facility = None
17+
18+
def is_assigned(self):
19+
return self.facility is not None
20+
21+
def distance_from_facility(self):
22+
"""
23+
Get distance from the facility.
24+
25+
Returns:
26+
distance in meters
27+
28+
Raises:
29+
RuntimeError: if no facility is assigned
30+
"""
31+
if self.facility is None:
32+
raise RuntimeError("No facility is assigned.")
33+
return self.facility.location.get_distance_to(self.location)
34+
35+
def __str__(self):
36+
return f"Consumer {self.id} ({self.demand} dem)"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class Facility:
2+
"""
3+
Facility satisfies consumers' demand. Cumulative demand of all consumers assigned to this facility must not exceed
4+
the facility's capacity. This requirement is expressed by the facility capacity constraint.
5+
"""
6+
7+
def __init__(self, id=None, location=None, setup_cost=None, capacity=None):
8+
self.id = id
9+
self.location = location
10+
self.setup_cost = setup_cost
11+
self.capacity = capacity
12+
13+
def get_used_capacity(self):
14+
return sum(consumer.demand for consumer in self.consumers)
15+
16+
def is_used(self):
17+
return len(self.consumers) > 0
18+
19+
def __str__(self):
20+
return f"Facility {self.id} (${self.setup_cost}, {self.capacity} cap)"
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
3+
from examples.object_oriented.facility_location.domain import Location
4+
5+
class FacilityLocationDomain:
6+
def __init__(self, facilities=None, consumers=None, south_west_corner=None, north_east_corner=None):
7+
self.facilities = facilities if facilities is not None else []
8+
self.consumers = consumers if consumers is not None else []
9+
self.score = None
10+
self.south_west_corner = south_west_corner
11+
self.north_east_corner = north_east_corner
12+
# Constraint weight overrides placeholder
13+
self.constraint_weight_overrides = {"distance from facility": (0, 6)} # (hard, soft) weights
14+
15+
def print_metrics(self):
16+
if self.consumers is None or len(self.consumers) == 0:
17+
print("No cosumers. Check domain initialization.")
18+
19+
total_distance = 0
20+
facilities_usages = {}
21+
for consumer in self.consumers:
22+
total_distance += consumer.distance_from_facility()
23+
24+
if consumer.facility not in facilities_usages:
25+
facilities_usages[consumer.facility] = 0
26+
27+
facilities_usages[consumer.facility] += consumer.demand
28+
29+
total_setup_cost = 0
30+
for facility in facilities_usages.keys():
31+
total_setup_cost += facility.setup_cost
32+
33+
print("Facility usages:")
34+
for facility in facilities_usages:
35+
facility_id = facility.id
36+
facility_usage = facilities_usages[facility]
37+
facility_capacity = facility.capacity
38+
print("{}: {}/{}".format(facility_id, facility_usage, facility_capacity))
39+
print("Total distance: {} m".format(total_distance))
40+
print("Total setup cost: {}$".format(total_setup_cost))
41+
42+
43+
44+
@classmethod
45+
def empty(cls):
46+
return cls(
47+
facilities=[],
48+
consumers=[],
49+
south_west_corner=Location(-90, -180),
50+
north_east_corner=Location(90, 180)
51+
)
52+
53+
def get_bounds(self):
54+
return [self.south_west_corner, self.north_east_corner]
55+
56+
def get_total_cost(self):
57+
return sum(facility.setup_cost for facility in self.facilities if facility.is_used())
58+
59+
def get_potential_cost(self):
60+
return sum(facility.setup_cost for facility in self.facilities)
61+
62+
def get_total_distance(self):
63+
distance = sum(consumer.distance_from_facility()
64+
for consumer in self.consumers if consumer.is_assigned())
65+
return f"{distance // 1000} km"
66+
67+
def __str__(self):
68+
return (f"FacilityLocationProblem{{facilities: {len(self.facilities)}, "
69+
f"consumers: {len(self.consumers)}, score: {self.score}}}")
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import math
2+
3+
class Location:
4+
# Approximate Metric Equivalents for Degrees. At the equator for longitude and for latitude anywhere,
5+
# the following approximations are valid: 1° = 111 km (or 60 nautical miles) 0.1° = 11.1 km.
6+
METERS_PER_DEGREE = 111000
7+
8+
def __init__(self, latitude, longitude):
9+
self.latitude = latitude
10+
self.longitude = longitude
11+
12+
def __str__(self):
13+
return f"[{self.latitude:.4f}N, {self.longitude:.4f}E]"
14+
15+
def get_distance_to(self, other):
16+
latitude_diff = other.latitude - self.latitude
17+
longitude_diff = other.longitude - self.longitude
18+
distance_degrees = math.sqrt(latitude_diff**2 + longitude_diff**2)
19+
return math.ceil(distance_degrees * self.METERS_PER_DEGREE)

0 commit comments

Comments
 (0)