Skip to content

Commit

Permalink
Merge branch 'Serious-senpai:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
khoint5304 authored Dec 29, 2023
2 parents 108bd7a + bd66eb9 commit e809977
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 108 deletions.
12 changes: 6 additions & 6 deletions ts/d2d/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
class SolutionMetricsMixin(BaseMulticostComparison):

__slots__ = (
"__cost",
"_cost",
"drone_timespans",
"drone_waiting_times",
"technician_timespans",
"technician_waiting_times",
)
if TYPE_CHECKING:
__cost: Optional[Tuple[float, float]]
_cost: Optional[Tuple[float, float]]
drone_timespans: Final[Tuple[float, ...]]
drone_waiting_times: Final[Tuple[Tuple[float, ...], ...]]
technician_timespans: Final[Tuple[float, ...]]
Expand All @@ -41,14 +41,14 @@ def __init__(
self.technician_timespans = technician_timespans
self.technician_waiting_times = technician_waiting_times

self.__cost = None
self._cost = None

def cost(self) -> Tuple[float, float]:
"""The cost of the solution that this object represents."""
if self.__cost is None:
self.__cost = (
if self._cost is None:
self._cost = (
max(*self.drone_timespans, *self.technician_timespans),
sum(sum(t) for t in self.drone_waiting_times) + sum(self.technician_waiting_times),
)

return self.__cost
return self._cost
6 changes: 6 additions & 0 deletions ts/d2d/neighborhoods/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@


__all__ = ("SolutionFactory",)
FINE_COEFFICIENT = 10 ** 9


class SolutionFactory(SolutionMetricsMixin):
Expand Down Expand Up @@ -81,6 +82,11 @@ def __init__(
self.__update_drones = update_drones
self.__update_technicians = update_technicians

def add_violation(self, violation: float) -> None:
fined = FINE_COEFFICIENT * violation
cost = (self.cost()[0] + fined, self.cost()[1] + fined)
self._cost = cost

def from_solution(self, __s: D2DPathSolution, /) -> D2DPathSolution:
if len(self.__append_drones) + len(self.__update_drones) > 0:
_drone_paths = list(list(paths) for paths in __s.drone_paths)
Expand Down
133 changes: 83 additions & 50 deletions ts/d2d/neighborhoods/insert.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,6 @@ def create_new() -> None:
_first_path[first_point:first_point + neighborhood.length] = []
_second_path = (0,) + first_path[first_point:first_point + neighborhood.length] + (0,)

if solution.calculate_total_weight(_second_path) > second_config.capacity:
continue

first_arrival_timestamps = solution.calculate_drone_arrival_timestamps(
_first_path,
config_index=solution.drone_config_mapping[first_drone],
Expand Down Expand Up @@ -204,6 +201,10 @@ def create_new() -> None:
drone_timespans=tuple(_drone_timespans),
drone_waiting_times=tuple(tuple(w) for w in _drone_waiting_times),
)
violation = (solution.calculate_total_weight(_second_path) - second_config.capacity) / second_config.capacity
if violation > 0:
factory.add_violation(violation)

if factory.add_to_pareto_set(results)[0]:
swaps_mapping[factory] = ((first_path[first_point], first_path[first_point + neighborhood.length - 1]), 0)

Expand Down Expand Up @@ -232,23 +233,6 @@ def create_new() -> None:
offset=solution.drone_arrival_timestamps[second_drone][second_path_index - 1][-1] if second_path_index > 0 else 0.0,
)

# The first drone path was shortened, hence it will not violate any constraints as long as the initial
# path is also a feasible one

if solution.calculate_total_weight(p2) > second_config.capacity:
continue

if isinstance(second_config, DroneEnduranceConfig):
if solution.calculate_drone_flight_duration(p2, arrival_timestamps=second_arrival_timestamps) > second_config.fixed_time:
continue

# No need to check for second_config.fixed_distance since the inserted segment also comes from another
# drone path

else:
if solution.calculate_drone_energy_consumption(p2, config_index=solution.drone_config_mapping[second_drone]) > second_config.battery:
continue

_drone_timespans = list(solution.drone_timespans)
_drone_timespans[first_drone] += first_arrival_timestamps[-1] - solution.drone_arrival_timestamps[first_drone][first_path_index][-1]
_drone_timespans[second_drone] += second_arrival_timestamps[-1] - solution.drone_arrival_timestamps[second_drone][second_path_index][-1]
Expand All @@ -264,6 +248,33 @@ def create_new() -> None:
drone_timespans=tuple(_drone_timespans),
drone_waiting_times=tuple(tuple(w) for w in _drone_waiting_times),
)

# The first drone path was shortened, hence it will not violate any constraints as long as the initial
# path is also a feasible one

violation = (solution.calculate_total_weight(p2) - second_config.capacity) / second_config.capacity
if violation > 0:
factory.add_violation(violation)

if isinstance(second_config, DroneEnduranceConfig):
violation = (
solution.calculate_drone_flight_duration(p2, arrival_timestamps=second_arrival_timestamps)
- second_config.fixed_time
) / second_config.fixed_time
if violation > 0:
factory.add_violation(violation)

# No need to check for second_config.fixed_distance since the inserted segment also comes from another
# drone path

else:
violation = (
solution.calculate_drone_energy_consumption(p2, config_index=solution.drone_config_mapping[second_drone])
- second_config.battery
) / second_config.battery
if violation > 0:
factory.add_violation(violation)

if factory.add_to_pareto_set(results)[0]:
swaps_mapping[factory] = ((first_path[first_point], first_path[first_point + neighborhood.length - 1]), second_path[second_location])

Expand Down Expand Up @@ -348,30 +359,13 @@ def create_new() -> None:
_tech_path[tech_point:tech_point + neighborhood.length] = []
_drone_path = (0,) + tuple(tech_path[tech_point:tech_point + neighborhood.length]) + (0,)

if solution.calculate_total_weight(_drone_path) > drone_config.capacity:
return

tech_arrival_timestamps = solution.calculate_technician_arrival_timestamps(_tech_path)
drone_arrival_timestamps = solution.calculate_drone_arrival_timestamps(
_drone_path,
config_index=solution.drone_config_mapping[drone],
offset=solution.drone_timespans[drone],
)

if isinstance(drone_config, DroneEnduranceConfig):
if solution.calculate_drone_flight_duration(_drone_path, arrival_timestamps=drone_arrival_timestamps) > drone_config.fixed_time:
return

if solution.calculate_required_range(_drone_path) > drone_config.fixed_distance:
return

else:
if solution.calculate_drone_energy_consumption(
_drone_path,
config_index=solution.drone_config_mapping[drone],
) > drone_config.battery:
return

_technician_timespans = list(solution.technician_timespans)
_technician_timespans[technician] = tech_arrival_timestamps[-1]

Expand All @@ -397,6 +391,33 @@ def create_new() -> None:
drone_timespans=tuple(_drone_timespans),
drone_waiting_times=tuple(tuple(w) for w in _drone_total_waiting_times),
)

violation = (solution.calculate_total_weight(_drone_path) - drone_config.capacity) / drone_config.capacity
if violation > 0:
factory.add_violation(violation)

if isinstance(drone_config, DroneEnduranceConfig):
violation = (
solution.calculate_drone_flight_duration(_drone_path, arrival_timestamps=drone_arrival_timestamps)
- drone_config.fixed_time
) / drone_config.fixed_time
if violation > 0:
factory.add_violation(violation)

violation = (solution.calculate_required_range(_drone_path) - drone_config.fixed_distance) / drone_config.fixed_distance
if violation > 0:
factory.add_violation(violation)

else:
violation = (
solution.calculate_drone_energy_consumption(
_drone_path,
config_index=solution.drone_config_mapping[drone],
) - drone_config.battery
) / drone_config.battery
if violation > 0:
factory.add_violation(violation)

if factory.add_to_pareto_set(results)[0]:
swaps_mapping[factory] = ((tech_path[tech_point], tech_path[tech_point + neighborhood.length - 1]), 0)

Expand All @@ -418,19 +439,6 @@ def create_new() -> None:
config_index=solution.drone_config_mapping[drone],
offset=solution.drone_arrival_timestamps[drone][drone_path_index - 1][-1] if drone_path_index > 0 else 0.0,
)
if solution.calculate_total_weight(_drone_path) > drone_config.capacity:
continue

if isinstance(drone_config, DroneEnduranceConfig):
if solution.calculate_drone_flight_duration(_drone_path, arrival_timestamps=drone_arrival_timestamps) > drone_config.fixed_time:
continue

if solution.calculate_required_range(_drone_path) > drone_config.fixed_distance:
continue

else:
if solution.calculate_drone_energy_consumption(_drone_path, config_index=solution.drone_config_mapping[drone]) > drone_config.battery:
continue

_technician_timespans = list(solution.technician_timespans)
_technician_timespans[technician] = tech_arrival_timestamps[-1]
Expand All @@ -455,6 +463,31 @@ def create_new() -> None:
drone_timespans=tuple(_drone_timespans),
drone_waiting_times=tuple(tuple(w) for w in _drone_total_waiting_times),
)

violation = (solution.calculate_total_weight(_drone_path) - drone_config.capacity) / drone_config.capacity
if violation > 0:
factory.add_violation(violation)

if isinstance(drone_config, DroneEnduranceConfig):
violation = (
solution.calculate_drone_flight_duration(_drone_path, arrival_timestamps=drone_arrival_timestamps)
- drone_config.fixed_time
) / drone_config.fixed_time
if violation > 0:
factory.add_violation(violation)

violation = (solution.calculate_required_range(_drone_path) - drone_config.fixed_distance) / drone_config.fixed_distance
if violation > 0:
factory.add_violation(violation)

else:
violation = (
solution.calculate_drone_energy_consumption(_drone_path, config_index=solution.drone_config_mapping[drone])
- drone_config.battery
) / drone_config.battery
if violation > 0:
factory.add_violation(violation)

if factory.add_to_pareto_set(results)[0]:
swaps_mapping[factory] = ((tech_path[tech_point], tech_path[tech_point + neighborhood.length - 1]), drone_path[drone_location])

Expand Down
Loading

0 comments on commit e809977

Please sign in to comment.