diff --git a/adf_core_python/core/agent/info/scenario_info.py b/adf_core_python/core/agent/info/scenario_info.py index 099b7f2..fe085bf 100644 --- a/adf_core_python/core/agent/info/scenario_info.py +++ b/adf_core_python/core/agent/info/scenario_info.py @@ -1,4 +1,5 @@ from enum import Enum +from typing import Any from adf_core_python.core.config.config import Config @@ -57,7 +58,7 @@ def get_mode(self) -> Mode: """ return self._mode - def get_config_value(self, key: str, default: str) -> str: + def get_value(self, key: str, default: Any) -> Any: """ Get the value of the configuration @@ -65,10 +66,12 @@ def get_config_value(self, key: str, default: str) -> str: ---------- key : str Key of the configuration + default : Any + Default value of the configuration Returns ------- - str + Any Value of the configuration """ return self._config.get_value(key, default) diff --git a/adf_core_python/core/agent/info/world_info.py b/adf_core_python/core/agent/info/world_info.py index 1ca617d..9a66517 100644 --- a/adf_core_python/core/agent/info/world_info.py +++ b/adf_core_python/core/agent/info/world_info.py @@ -53,14 +53,16 @@ def get_entity(self, entity_id: EntityID) -> Optional[Entity]: """ return self._world_model.get_entity(entity_id) - def get_entity_ids_of_type(self, entity_type: type[Entity]) -> list[EntityID]: + def get_entity_ids_of_types( + self, entity_types: list[type[Entity]] + ) -> list[EntityID]: """ - Get the entity IDs of the specified type + Get the entity IDs of the specified types Parameters ---------- - entity_type : type[Entity] - Entity type + entity_types : list[type[Entity]] + List of entity types Returns ------- @@ -69,7 +71,85 @@ def get_entity_ids_of_type(self, entity_type: type[Entity]) -> list[EntityID]: """ entity_ids: list[EntityID] = [] for entity in self._world_model.get_entities(): - if isinstance(entity, entity_type): + if any(isinstance(entity, entity_type) for entity_type in entity_types): entity_ids.append(entity.get_id()) return entity_ids + + def get_entities_of_types(self, entity_types: list[type[Entity]]) -> list[Entity]: + """ + Get the entities of the specified types + + Parameters + ---------- + entity_types : list[type[Entity]] + List of entity types + + Returns + ------- + list[Entity] + Entities + """ + entities: list[Entity] = [] + for entity in self._world_model.get_entities(): + if any(isinstance(entity, entity_type) for entity_type in entity_types): + entities.append(entity) + + return entities + + def get_distance(self, entity_id1: EntityID, entity_id2: EntityID) -> float: + """ + Get the distance between two entities + + Parameters + ---------- + entity_id1 : EntityID + Entity ID 1 + entity_id2 : EntityID + Entity ID 2 + + Returns + ------- + float + Distance + + Raises + ------ + ValueError + If one or both entities are invalid or the location is invalid + """ + entity1: Optional[Entity] = self.get_entity(entity_id1) + entity2: Optional[Entity] = self.get_entity(entity_id2) + if entity1 is None or entity2 is None: + raise ValueError( + f"One or both entities are invalid: entity_id1={entity_id1}, entity_id2={entity_id2}, entity1={entity1}, entity2={entity2}" + ) + + location1_x, location1_y = entity1.get_location() + location2_x, location2_y = entity2.get_location() + if ( + location1_x is None + or location1_y is None + or location2_x is None + or location2_y is None + ): + raise ValueError( + f"Invalid location: entity_id1={entity_id1}, entity_id2={entity_id2}, location1_x={location1_x}, location1_y={location1_y}, location2_x={location2_x}, location2_y={location2_y}" + ) + + distance: float = ( + (location1_x - location2_x) ** 2 + (location1_y - location2_y) ** 2 + ) ** 0.5 + + return distance + + def get_change_set(self) -> ChangeSet: + """ + Get the change set + + Returns + ------- + ChangeSet + Change set + """ + return self._change_set diff --git a/adf_core_python/core/component/module/algorithm/clustering.py b/adf_core_python/core/component/module/algorithm/clustering.py new file mode 100644 index 0000000..0a6bfdc --- /dev/null +++ b/adf_core_python/core/component/module/algorithm/clustering.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import TYPE_CHECKING + +from adf_core_python.core.component.module.abstract_module import AbstractModule + +if TYPE_CHECKING: + from rcrs_core.entities.entity import Entity + from rcrs_core.worldmodel.entityID import EntityID + + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.develop.develop_data import DevelopData + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.agent.module.module_manager import ModuleManager + from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData + + +class Clustering(AbstractModule): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + @abstractmethod + def get_cluster_number(self) -> int: + pass + + @abstractmethod + def get_cluster_index(self, entity_id: EntityID) -> int: + pass + + @abstractmethod + def get_cluster_entities(self, cluster_index: int) -> list[Entity]: + pass + + @abstractmethod + def get_cluster_entity_ids(self, cluster_index: int) -> list[EntityID]: + pass + + @abstractmethod + def calculate(self) -> Clustering: + pass + + def precompute(self, precompute_data: PrecomputeData) -> Clustering: + super().precompute(precompute_data) + return self + + def resume(self, precompute_data: PrecomputeData) -> Clustering: + super().resume(precompute_data) + return self + + def prepare(self) -> Clustering: + super().prepare() + return self + + def update_info(self, message_manager: MessageManager) -> Clustering: + super().update_info(message_manager) + return self diff --git a/adf_core_python/core/component/module/complex/target_detector.py b/adf_core_python/core/component/module/complex/target_detector.py index 2a405af..29fb4df 100644 --- a/adf_core_python/core/component/module/complex/target_detector.py +++ b/adf_core_python/core/component/module/complex/target_detector.py @@ -1,7 +1,7 @@ from __future__ import annotations from abc import abstractmethod -from typing import TYPE_CHECKING, Generic, TypeVar +from typing import TYPE_CHECKING, Generic, Optional, TypeVar from rcrs_core.entities.entity import Entity @@ -35,7 +35,7 @@ def __init__( ) @abstractmethod - def get_target_entity_id(self) -> EntityID: + def get_target_entity_id(self) -> Optional[EntityID]: pass @abstractmethod diff --git a/adf_core_python/implement/extend_action/default_extend_action_transport.py b/adf_core_python/implement/extend_action/default_extend_action_transport.py index f770b48..aa75d22 100644 --- a/adf_core_python/implement/extend_action/default_extend_action_transport.py +++ b/adf_core_python/implement/extend_action/default_extend_action_transport.py @@ -23,6 +23,7 @@ from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning +# TODO: refactor this class class DefaultExtendActionTransport(ExtAction): def __init__( self, @@ -99,7 +100,7 @@ def calc(self) -> ExtAction: agent: AmbulanceTeamEntity = cast( AmbulanceTeamEntity, self.agent_info.get_myself() ) - transport_human: Human = self.agent_info.some_one_on_board() + transport_human: Optional[Human] = self.agent_info.some_one_on_board() if transport_human is not None: self.result = self.calc_unload( agent, self._path_planning, transport_human, self._target_entity_id @@ -134,9 +135,7 @@ def calc_rescue( target_position = human.get_position() if agent_position == target_position: - if isinstance(human, Civilian) and ( - human.get_buriedness() is not None and human.get_buriedness() > 0 - ): + if isinstance(human, Civilian) and ((human.get_buriedness() or 0) > 0): return ActionLoad(human.get_id()) else: path = path_planning.get_path(agent_position, target_position) @@ -176,9 +175,7 @@ def calc_unload( if isinstance(position, Refuge): return ActionUnload() else: - path = path_planning.get_path( - agent_position, self.world_info.get_entity_ids_of_type(Refuge) - ) + path = self.get_nearest_refuge_path(agent, path_planning) if path is not None and len(path) > 0: return ActionMove(path) @@ -191,7 +188,7 @@ def calc_unload( human = cast(Human, target_entity) if human.get_position() is not None: return self.calc_refuge_action( - agent, path_planning, [human.get_position()], True + agent, path_planning, human.get_position(), True ) path = self.get_nearest_refuge_path(agent, path_planning) if path is not None and len(path) > 0: @@ -207,7 +204,7 @@ def calc_refuge_action( is_unload: bool, ) -> Optional[ActionMove | ActionUnload | ActionRest]: position = human.get_position() - refuges = self.world_info.get_entity_ids_of_type(Refuge) + refuges = self.world_info.get_entity_ids_of_types([Refuge]) size = len(refuges) if position in refuges: @@ -242,7 +239,7 @@ def get_nearest_refuge_path( self, human: Human, path_planning: PathPlanning ) -> list[EntityID]: position = human.get_position() - refuges = self.world_info.get_entity_ids_of_type(Refuge) + refuges = self.world_info.get_entity_ids_of_types([Refuge]) nearest_path = None for refuge_id in refuges: diff --git a/adf_core_python/implement/module/astar_path_planning.py b/adf_core_python/implement/module/algorithm/a_star_path_planning.py similarity index 100% rename from adf_core_python/implement/module/astar_path_planning.py rename to adf_core_python/implement/module/algorithm/a_star_path_planning.py diff --git a/adf_core_python/implement/module/algorithm/k_means_clustering.py b/adf_core_python/implement/module/algorithm/k_means_clustering.py new file mode 100644 index 0000000..afb18be --- /dev/null +++ b/adf_core_python/implement/module/algorithm/k_means_clustering.py @@ -0,0 +1,123 @@ +import numpy as np +from rcrs_core.connection.URN import Entity as EntityURN +from rcrs_core.entities.ambulanceCenter import AmbulanceCentreEntity +from rcrs_core.entities.building import Building +from rcrs_core.entities.entity import Entity +from rcrs_core.entities.fireStation import FireStationEntity +from rcrs_core.entities.gassStation import GasStation +from rcrs_core.entities.hydrant import Hydrant +from rcrs_core.entities.policeOffice import PoliceOfficeEntity +from rcrs_core.entities.refuge import Refuge +from rcrs_core.entities.road import Road +from rcrs_core.worldmodel.entityID import EntityID +from sklearn.cluster import KMeans + +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.clustering import Clustering + + +class KMeansClustering(Clustering): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + match agent_info.get_myself().get_urn(): + case EntityURN.AMBULANCE_TEAM: + self._cluster_number = scenario_info.get_value( + "scenario.agents.at", + 1, + ) + case EntityURN.POLICE_FORCE: + self._cluster_number = scenario_info.get_value( + "scenario.agents.pf", + 1, + ) + case EntityURN.FIRE_BRIGADE: + self._cluster_number = scenario_info.get_value( + "scenario.agents.fb", + 1, + ) + case _: + self._cluster_number = 1 + + sorted_entities = sorted( + world_info.get_entities_of_types( + [ + agent_info.get_myself().__class__, + ] + ), + key=lambda entity: entity.get_id().get_value(), + ) + self.entity_cluster_indices = { + entity.get_id(): idx for idx, entity in enumerate(sorted_entities) + } + + self.cluster_entities: list[list[Entity]] = [] + self.entities: list[Entity] = world_info.get_entities_of_types( + [ + AmbulanceCentreEntity, + FireStationEntity, + GasStation, + Hydrant, + PoliceOfficeEntity, + Refuge, + Road, + Building, + ] + ) + + def calculate(self) -> Clustering: + return self + + def get_cluster_number(self) -> int: + return self._cluster_number + + def get_cluster_index(self, entity_id: EntityID) -> int: + return self.entity_cluster_indices.get(entity_id, 0) + + def get_cluster_entities(self, cluster_index: int) -> list[Entity]: + if cluster_index >= len(self.cluster_entities): + return [] + return self.cluster_entities[cluster_index] + + def get_cluster_entity_ids(self, cluster_index: int) -> list[EntityID]: + if cluster_index >= len(self.cluster_entities): + return [] + return [entity.get_id() for entity in self.cluster_entities[cluster_index]] + + def prepare(self) -> Clustering: + super().prepare() + if self.get_count_prepare() > 1: + return self + self.cluster_entities = self.create_cluster(self._cluster_number, self.entities) + return self + + def create_cluster( + self, cluster_number: int, entities: list[Entity] + ) -> list[list[Entity]]: + kmeans = KMeans(n_clusters=cluster_number) + entity_positions: np.ndarray = np.array([]) + for entity in entities: + location1_x, location1_y = entity.get_location() + if location1_x is None or location1_y is None: + continue + entity_positions = np.append(entity_positions, [location1_x, location1_y]) + + kmeans.fit(entity_positions.reshape(-1, 2)) + + clusters: list[list[Entity]] = [[] for _ in range(cluster_number)] + for entity, label in zip(entities, kmeans.labels_): + clusters[label].append(entity) + + return clusters diff --git a/adf_core_python/implement/module/complex/default_human_detector.py b/adf_core_python/implement/module/complex/default_human_detector.py new file mode 100644 index 0000000..05e58ce --- /dev/null +++ b/adf_core_python/implement/module/complex/default_human_detector.py @@ -0,0 +1,110 @@ +from typing import Optional, cast + +from rcrs_core.connection.URN import Entity as EntityURN +from rcrs_core.entities.entity import Entity +from rcrs_core.entities.human import Human +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.clustering import Clustering +from adf_core_python.core.component.module.complex.human_detector import HumanDetector + + +class DefaultHumanDetector(HumanDetector): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + self._clustering: Clustering = cast( + Clustering, + module_manager.get_module( + "DefaultHumanDetector.Clustering", + "adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering", + ), + ) + self.register_sub_module(self._clustering) + + self._result: Optional[EntityID] = None + + def calculate(self) -> HumanDetector: + transport_human: Optional[Human] = self._agent_info.some_one_on_board() + if transport_human is not None: + self._result = transport_human.get_id() + return self + + if self._result is not None: + if self._is_valid_human(self._result): + self._result = self._select_target() + + return self + + def _select_target(self) -> Optional[EntityID]: + if self._result is not None and self._is_valid_human(self._result): + return self._result + + cluster_index: int = self._clustering.get_cluster_index( + self._agent_info.get_entity_id() + ) + cluster_entities: list[Entity] = self._clustering.get_cluster_entities( + cluster_index + ) + cluster_valid_human_entities: list[Entity] = [ + entity + for entity in cluster_entities + if self._is_valid_human(entity.get_id()) + ] + if len(cluster_valid_human_entities) == 0: + return None + + nearest_human_entity: Optional[Entity] = None + nearest_distance: float = 10**10 + for entity in cluster_valid_human_entities: + distance: float = self._world_info.get_distance( + self._agent_info.get_entity_id(), + entity.get_id(), + ) + if distance < nearest_distance: + nearest_distance = distance + nearest_human_entity = entity + + return ( + nearest_human_entity.get_id() if nearest_human_entity is not None else None + ) + + def _is_valid_human(self, target_entity_id: EntityID) -> bool: + target: Optional[Entity] = self._world_info.get_entity(target_entity_id) + if target is None: + return False + if not isinstance(target, Human): + return False + hp: Optional[int] = target.get_hp() + if hp is None or hp <= 0: + return False + buriedness: Optional[int] = target.get_buriedness() + if buriedness is None or buriedness > 0: + return False + position_entity_id: Optional[EntityID] = target.get_position() + if position_entity_id is None: + return False + position: Optional[Entity] = self._world_info.get_entity(position_entity_id) + if position is None: + return False + urn: EntityURN = position.get_urn() + if urn == EntityURN.REFUGE or urn == EntityURN.AMBULANCE_TEAM: + return False + + return True + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._result diff --git a/adf_core_python/implement/module/complex/default_search.py b/adf_core_python/implement/module/complex/default_search.py new file mode 100644 index 0000000..3c35a4a --- /dev/null +++ b/adf_core_python/implement/module/complex/default_search.py @@ -0,0 +1,98 @@ +from typing import Optional, cast + +from rcrs_core.connection.URN import Entity as EntityURN +from rcrs_core.entities.entity import Entity +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.module.module_manager import ModuleManager +from adf_core_python.core.component.module.algorithm.clustering import Clustering +from adf_core_python.core.component.module.algorithm.path_planning import PathPlanning +from adf_core_python.core.component.module.complex.search import Search + + +class DefaultSearch(Search): + def __init__( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + module_manager: ModuleManager, + develop_data: DevelopData, + ) -> None: + super().__init__( + agent_info, world_info, scenario_info, module_manager, develop_data + ) + + self._unsearched_building_ids: set[EntityID] = set() + self._result: Optional[EntityID] = None + + self._clustering: Clustering = cast( + Clustering, + module_manager.get_module( + "DefaultSearch.Clustering", + "adf_core_python.implement.module.algorithm.k_means_clustering.KMeansClustering", + ), + ) + + self._path_planning: PathPlanning = cast( + PathPlanning, + module_manager.get_module( + "DefaultSearch.PathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", + ), + ) + + self.register_sub_module(self._clustering) + self.register_sub_module(self._path_planning) + + def update_info(self, message_manager: MessageManager) -> Search: + super().update_info(message_manager) + if self.get_count_update_info() > 1: + return self + + searched_building_ids = self._world_info.get_change_set().get_changed_entities() + self._unsearched_building_ids = self._unsearched_building_ids.difference( + searched_building_ids + ) + + if len(self._unsearched_building_ids) == 0: + self._unsearched_building_ids = self._get_search_targets() + + return self + + def calculate(self) -> Search: + nearest_building_id: Optional[EntityID] = None + nearest_distance: Optional[float] = None + for building_id in self._unsearched_building_ids: + distance = self._world_info.get_distance( + self._agent_info.get_entity_id(), building_id + ) + if nearest_distance is None or distance < nearest_distance: + nearest_building_id = building_id + nearest_distance = distance + self._result = nearest_building_id + return self + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._result + + def _get_search_targets(self) -> set[EntityID]: + cluster_index: int = self._clustering.get_cluster_index( + self._agent_info.get_entity_id() + ) + cluster_entities: list[Entity] = self._clustering.get_cluster_entities( + cluster_index + ) + building_entity_ids: list[EntityID] = [ + entity.get_id() + for entity in cluster_entities + if entity.get_urn() == EntityURN.BUILDING + and entity.get_urn() != EntityURN.REFUGE + ] + + return set(building_entity_ids) diff --git a/adf_core_python/implement/tactics/default_tactics_police_force.py b/adf_core_python/implement/tactics/default_tactics_police_force.py index ecd2134..bd4f29d 100644 --- a/adf_core_python/implement/tactics/default_tactics_police_force.py +++ b/adf_core_python/implement/tactics/default_tactics_police_force.py @@ -31,7 +31,7 @@ def initialize( ) -> None: # world_info.index_class() self._clear_distance = int( - scenario_info.get_config_value("clear.repair.distance", "null") + scenario_info.get_value("clear.repair.distance", "null") ) match scenario_info.get_mode(): diff --git a/poetry.lock b/poetry.lock index b074efb..2f0e6c9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -22,6 +22,17 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "joblib" +version = "1.4.2" +description = "Lightweight pipelining with Python functions" +optional = false +python-versions = ">=3.8" +files = [ + {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, + {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, +] + [[package]] name = "mslex" version = "1.2.0" @@ -90,6 +101,68 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "numpy" +version = "2.1.1" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "numpy-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8a0e34993b510fc19b9a2ce7f31cb8e94ecf6e924a40c0c9dd4f62d0aac47d9"}, + {file = "numpy-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7dd86dfaf7c900c0bbdcb8b16e2f6ddf1eb1fe39c6c8cca6e94844ed3152a8fd"}, + {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:5889dd24f03ca5a5b1e8a90a33b5a0846d8977565e4ae003a63d22ecddf6782f"}, + {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:59ca673ad11d4b84ceb385290ed0ebe60266e356641428c845b39cd9df6713ab"}, + {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ce49a34c44b6de5241f0b38b07e44c1b2dcacd9e36c30f9c2fcb1bb5135db7"}, + {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913cc1d311060b1d409e609947fa1b9753701dac96e6581b58afc36b7ee35af6"}, + {file = "numpy-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:caf5d284ddea7462c32b8d4a6b8af030b6c9fd5332afb70e7414d7fdded4bfd0"}, + {file = "numpy-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:57eb525e7c2a8fdee02d731f647146ff54ea8c973364f3b850069ffb42799647"}, + {file = "numpy-2.1.1-cp310-cp310-win32.whl", hash = "sha256:9a8e06c7a980869ea67bbf551283bbed2856915f0a792dc32dd0f9dd2fb56728"}, + {file = "numpy-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:d10c39947a2d351d6d466b4ae83dad4c37cd6c3cdd6d5d0fa797da56f710a6ae"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0"}, + {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95"}, + {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca"}, + {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf"}, + {file = "numpy-2.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e"}, + {file = "numpy-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2"}, + {file = "numpy-2.1.1-cp311-cp311-win32.whl", hash = "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d"}, + {file = "numpy-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f"}, + {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521"}, + {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b"}, + {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201"}, + {file = "numpy-2.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a"}, + {file = "numpy-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313"}, + {file = "numpy-2.1.1-cp312-cp312-win32.whl", hash = "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed"}, + {file = "numpy-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136"}, + {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0"}, + {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb"}, + {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df"}, + {file = "numpy-2.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78"}, + {file = "numpy-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556"}, + {file = "numpy-2.1.1-cp313-cp313-win32.whl", hash = "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b"}, + {file = "numpy-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f"}, + {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468"}, + {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef"}, + {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f"}, + {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c"}, + {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7be6a07520b88214ea85d8ac8b7d6d8a1839b0b5cb87412ac9f49fa934eb15d5"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:52ac2e48f5ad847cd43c4755520a2317f3380213493b9d8a4c5e37f3b87df504"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a95ca3560a6058d6ea91d4629a83a897ee27c00630aed9d933dff191f170cd"}, + {file = "numpy-2.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:99f4a9ee60eed1385a86e82288971a51e71df052ed0b2900ed30bc840c0f2e39"}, + {file = "numpy-2.1.1.tar.gz", hash = "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd"}, +] + [[package]] name = "packaging" version = "24.1" @@ -321,6 +394,101 @@ files = [ {file = "ruff-0.4.10.tar.gz", hash = "sha256:3aa4f2bc388a30d346c56524f7cacca85945ba124945fe489952aadb6b5cd804"}, ] +[[package]] +name = "scikit-learn" +version = "1.5.2" +description = "A set of python modules for machine learning and data mining" +optional = false +python-versions = ">=3.9" +files = [ + {file = "scikit_learn-1.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:299406827fb9a4f862626d0fe6c122f5f87f8910b86fe5daa4c32dcd742139b6"}, + {file = "scikit_learn-1.5.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:2d4cad1119c77930b235579ad0dc25e65c917e756fe80cab96aa3b9428bd3fb0"}, + {file = "scikit_learn-1.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c412ccc2ad9bf3755915e3908e677b367ebc8d010acbb3f182814524f2e5540"}, + {file = "scikit_learn-1.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a686885a4b3818d9e62904d91b57fa757fc2bed3e465c8b177be652f4dd37c8"}, + {file = "scikit_learn-1.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:c15b1ca23d7c5f33cc2cb0a0d6aaacf893792271cddff0edbd6a40e8319bc113"}, + {file = "scikit_learn-1.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03b6158efa3faaf1feea3faa884c840ebd61b6484167c711548fce208ea09445"}, + {file = "scikit_learn-1.5.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:1ff45e26928d3b4eb767a8f14a9a6efbf1cbff7c05d1fb0f95f211a89fd4f5de"}, + {file = "scikit_learn-1.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f763897fe92d0e903aa4847b0aec0e68cadfff77e8a0687cabd946c89d17e675"}, + {file = "scikit_learn-1.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8b0ccd4a902836493e026c03256e8b206656f91fbcc4fde28c57a5b752561f1"}, + {file = "scikit_learn-1.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:6c16d84a0d45e4894832b3c4d0bf73050939e21b99b01b6fd59cbb0cf39163b6"}, + {file = "scikit_learn-1.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f932a02c3f4956dfb981391ab24bda1dbd90fe3d628e4b42caef3e041c67707a"}, + {file = "scikit_learn-1.5.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3b923d119d65b7bd555c73be5423bf06c0105678ce7e1f558cb4b40b0a5502b1"}, + {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f60021ec1574e56632be2a36b946f8143bf4e5e6af4a06d85281adc22938e0dd"}, + {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:394397841449853c2290a32050382edaec3da89e35b3e03d6cc966aebc6a8ae6"}, + {file = "scikit_learn-1.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:57cc1786cfd6bd118220a92ede80270132aa353647684efa385a74244a41e3b1"}, + {file = "scikit_learn-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:757c7d514ddb00ae249832fe87100d9c73c6ea91423802872d9e74970a0e40b9"}, + {file = "scikit_learn-1.5.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:52788f48b5d8bca5c0736c175fa6bdaab2ef00a8f536cda698db61bd89c551c1"}, + {file = "scikit_learn-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:643964678f4b5fbdc95cbf8aec638acc7aa70f5f79ee2cdad1eec3df4ba6ead8"}, + {file = "scikit_learn-1.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca64b3089a6d9b9363cd3546f8978229dcbb737aceb2c12144ee3f70f95684b7"}, + {file = "scikit_learn-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:3bed4909ba187aca80580fe2ef370d9180dcf18e621a27c4cf2ef10d279a7efe"}, + {file = "scikit_learn-1.5.2.tar.gz", hash = "sha256:b4237ed7b3fdd0a4882792e68ef2545d5baa50aca3bb45aa7df468138ad8f94d"}, +] + +[package.dependencies] +joblib = ">=1.2.0" +numpy = ">=1.19.5" +scipy = ">=1.6.0" +threadpoolctl = ">=3.1.0" + +[package.extras] +benchmark = ["matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "pandas (>=1.1.5)"] +build = ["cython (>=3.0.10)", "meson-python (>=0.16.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)"] +docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.3.4)", "memory_profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pydata-sphinx-theme (>=0.15.3)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)", "sphinx (>=7.3.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-design (>=0.5.0)", "sphinx-design (>=0.6.0)", "sphinx-gallery (>=0.16.0)", "sphinx-prompt (>=1.4.0)", "sphinx-remove-toctrees (>=1.0.0.post1)", "sphinxcontrib-sass (>=0.3.4)", "sphinxext-opengraph (>=0.9.1)"] +examples = ["matplotlib (>=3.3.4)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)"] +install = ["joblib (>=1.2.0)", "numpy (>=1.19.5)", "scipy (>=1.6.0)", "threadpoolctl (>=3.1.0)"] +maintenance = ["conda-lock (==2.5.6)"] +tests = ["black (>=24.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.9)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "polars (>=0.20.30)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.2.1)", "scikit-image (>=0.17.2)"] + +[[package]] +name = "scipy" +version = "1.14.1" +description = "Fundamental algorithms for scientific computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3"}, + {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d"}, + {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69"}, + {file = "scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad"}, + {file = "scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"}, + {file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"}, + {file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"}, + {file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"}, + {file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"}, + {file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"}, + {file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"}, + {file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"}, +] + +[package.dependencies] +numpy = ">=1.23.5,<2.3" + +[package.extras] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<=7.3.7)", "sphinx-design (>=0.4.0)"] +test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] + [[package]] name = "taskipy" version = "1.13.0" @@ -338,6 +506,17 @@ mslex = {version = ">=1.1.0,<2.0.0", markers = "sys_platform == \"win32\""} psutil = ">=5.7.2,<6.0.0" tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""} +[[package]] +name = "threadpoolctl" +version = "3.5.0" +description = "threadpoolctl" +optional = false +python-versions = ">=3.8" +files = [ + {file = "threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467"}, + {file = "threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107"}, +] + [[package]] name = "tomli" version = "2.0.1" @@ -385,4 +564,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "eaa45b01747d5585b46d2a0043f81002b7c88f0f7b6fc9920c3bda4c52506d6d" +content-hash = "88cd8709372c0c07eff53e51b00b3740518bb32125f72d9a2e7dd0717067d45a" diff --git a/pyproject.toml b/pyproject.toml index e9179d8..908a0b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ rcrs_core = {git = "https://github.com/adf-python/rcrs-core-python"} pyyaml = "^6.0.2" pytest = "^8.3.2" types-pyyaml = "^6.0.12.20240808" +scikit-learn = "^1.5.2" [tool.poetry.group.dev.dependencies] diff --git a/tests/core/agent/module/test_module_manager.py b/tests/core/agent/module/test_module_manager.py index 458791e..530b990 100644 --- a/tests/core/agent/module/test_module_manager.py +++ b/tests/core/agent/module/test_module_manager.py @@ -12,7 +12,7 @@ def test_can_get_module(self) -> None: config = ModuleConfig(config_file_path) config.set_value( "test_module", - "adf_core_python.implement.module.astar_path_planning.AStarPathPlanning", + "adf_core_python.implement.module.algorithm.a_star_path_planning.AStarPathPlanning", ) module_manager = self.create_module_manager(config) module = module_manager.get_module("test_module", "test_module")