Skip to content

Commit

Permalink
Merge branch 'main' into knpmp_opening_constraint_bug
Browse files Browse the repository at this point in the history
  • Loading branch information
jGaboardi committed Dec 10, 2023
2 parents 3228bf7 + 8800d2f commit 6c89c4f
Show file tree
Hide file tree
Showing 20 changed files with 280 additions and 246 deletions.
20 changes: 6 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,22 +86,14 @@ line-length = 88
line-length = 88
select = ["E", "F", "W", "I", "UP", "N", "B", "A", "C4", "SIM", "ARG"]
target-version = "py310"
ignore = [
"B006",
"B008",
"B009",
"B010",
"C408",
"E731",
"F401",
"F403",
"N803",
"N806",
"N999",
"UP007"
]
exclude = ["spopt/tests/*", "docs/*"]

[tool.ruff.per-file-ignores]
"*__init__.py" = [
"F401", # imported but unused
"F403", # star import; unable to detect undefined names
]

[tool.coverage.run]
source = ["./spopt"]

Expand Down
2 changes: 2 additions & 0 deletions spopt/BaseClass.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# ruff: noqa: N999

from abc import ABC, abstractmethod


Expand Down
16 changes: 12 additions & 4 deletions spopt/locate/base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# ruff: noqa: B009, B010

from abc import abstractmethod
from typing import TypeVar

Expand Down Expand Up @@ -325,8 +327,8 @@ def add_maximized_min_variable(obj: T_FacModel) -> None:
None
"""
D = pulp.LpVariable("D", lowBound=0, cat=pulp.LpContinuous)
setattr(obj, "disperse_var", D)
big_d = pulp.LpVariable("D", lowBound=0, cat=pulp.LpContinuous)
setattr(obj, "disperse_var", big_d)

@staticmethod
def add_set_covering_constraint(
Expand Down Expand Up @@ -846,7 +848,7 @@ def add_p_dispersion_interfacility_constraint(
"""
if hasattr(obj, "disperse_var") and hasattr(obj, "fac_vars"):
M = cost_matrix.max()
big_m = cost_matrix.max()
model = getattr(obj, "problem")

for i in range_facility:
Expand All @@ -857,7 +859,13 @@ def add_p_dispersion_interfacility_constraint(
dij = cost_matrix[i, j]
model += (
pulp.lpSum(
[(dij + M * (2 - obj.fac_vars[i] - obj.fac_vars[j]))]
[
(
dij
+ big_m
* (2 - obj.fac_vars[i] - obj.fac_vars[j])
)
]
)
>= obj.disperse_var
)
Expand Down
12 changes: 7 additions & 5 deletions spopt/locate/coverage.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# ruff: noqa: B009

import warnings

import numpy as np
Expand Down Expand Up @@ -97,7 +99,7 @@ class LSCP(LocateSolver, BaseOutputMixin):
aij : numpy.array
A cost matrix in the form of a 2D array between origins and destinations.
""" # noqa
""" # noqa: E501

def __init__(self, name: str, problem: pulp.LpProblem):
super().__init__(name, problem)
Expand Down Expand Up @@ -398,7 +400,7 @@ def from_geodataframe(
facility 3 serving 0 clients
facility 4 serving 0 clients
""" # noqa
""" # noqa: E501

demand_quantity_arr = None
if demand_quantity_col is not None:
Expand Down Expand Up @@ -580,7 +582,7 @@ class LSCPB(LocateSolver, BaseOutputMixin, BackupPercentageMixinMixin):
aij : numpy.array
A cost matrix in the form of a 2D array between origins and destinations.
""" # noqa
""" # noqa: E501

def __init__(
self,
Expand Down Expand Up @@ -861,7 +863,7 @@ def from_geodataframe(
All clients are covered by 1 facility because only one facility
is needed to solve the LSCP.
""" # noqa
""" # noqa: E501

predefined_facilities_arr = None
if predefined_facility_col is not None:
Expand Down Expand Up @@ -1018,7 +1020,7 @@ class MCLP(LocateSolver, BaseOutputMixin, CoveragePercentageMixin):
n_cli_uncov : int
The number of uncovered client locations.
""" # noqa
""" # noqa: E501

def __init__(self, name: str, problem: pulp.LpProblem):
super().__init__(name, problem)
Expand Down
6 changes: 4 additions & 2 deletions spopt/locate/p_center.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# ruff: noqa: B009

import warnings

import numpy as np
Expand Down Expand Up @@ -70,7 +72,7 @@ class PCenter(LocateSolver, BaseOutputMixin):
aij : numpy.array
A cost matrix in the form of a 2D array between origins and destinations.
""" # noqa
""" # noqa: E501

def __init__(self, name: str, problem: pulp.LpProblem, aij: np.array):
self.problem = problem
Expand Down Expand Up @@ -324,7 +326,7 @@ def from_geodataframe(
facility 3 serving 0 clients
facility 4 serving 26 clients
""" # noqa
""" # noqa: E501

predefined_facilities_arr = None
if predefined_facility_col is not None:
Expand Down
6 changes: 4 additions & 2 deletions spopt/locate/p_dispersion.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# ruff: noqa: B009

import warnings

import numpy as np
Expand Down Expand Up @@ -51,7 +53,7 @@ class PDispersion(LocateSolver):
A ``pulp`` instance of an optimization model that contains
constraints, variables, and an objective function.
""" # noqa
""" # noqa: E501

def __init__(self, name: str, problem: pulp.LpProblem, p_facilities: int):
self.p_facilities = p_facilities
Expand Down Expand Up @@ -280,7 +282,7 @@ def from_geodataframe(
facility y_0_ is selected
facility y_1_ is selected
""" # noqa
""" # noqa: E501

predefined_facilities_arr = None
if predefined_facility_col is not None:
Expand Down
23 changes: 12 additions & 11 deletions spopt/locate/p_median.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# ruff: noqa: B009, B010

import warnings
from typing import Union

import numpy as np
import pulp
Expand Down Expand Up @@ -78,14 +79,14 @@ class PMedian(LocateSolver, BaseOutputMixin, MeanDistanceMixin):
aij : numpy.array
A cost matrix in the form of a 2D array between origins and destinations.
""" # noqa
""" # noqa: E501

def __init__(
self,
name: str,
problem: pulp.LpProblem,
aij: np.array,
weights_sum: Union[int, float],
weights_sum: int | float,
):
self.aij = aij
self.ai_sum = weights_sum
Expand Down Expand Up @@ -434,7 +435,7 @@ def from_geodataframe(
facility 3 serving 0 clients
facility 4 serving 27 clients
""" # noqa
""" # noqa: E501

predefined_facilities_arr = None
if predefined_facility_col is not None:
Expand Down Expand Up @@ -545,7 +546,7 @@ def solve(self, solver: pulp.LpSolver, results: bool = True):

class KNearestPMedian(PMedian):
r"""
Implement the P-Median Model with Near-Far Cost Allocation and solve it.
Implement the P-Median Model with Near-Far Cost Allocation and solve it.
The model is adapted from :cite:`richard_2018`, can be formulated as:
.. math::
Expand All @@ -554,7 +555,7 @@ class KNearestPMedian(PMedian):
\displaystyle \textbf{Minimize} & \displaystyle \sum_{i \in I}\sum_{k \in k_{i}}{a_i d_{ik} X_{ik}} + \sum_{i \in I}{g_i (d_{i{k_i}} + 1)} && & (1) \\
\displaystyle \textbf{Subject To} & \sum_{k \in k_{i}}{X_{ik} + g_i = 1} && \forall i \in I & (2) \\
& \sum_{j \in J}{Y_j} = p && & (3) \\
& \sum_{i \in I}{a_i X_{ik}} \leq {Y_{k} c_{k}} && \forall k \in k_{i} & (4) \\
& \sum_{i \in I}{a_i X_{ik}} \leq {Y_{k} c_{k}} && \forall k \in k_{i} & (4) \\
& X_{ij} \leq Y_{j} && \forall i \in I \quad \forall j \in J & (5) \\
& X_{ij} \in \{0, 1\} && \forall i \in I \quad \forall j \in J & (6) \\
& Y_j \in \{0, 1\} && \forall j \in J & (7) \\
Expand All @@ -564,7 +565,7 @@ class KNearestPMedian(PMedian):
&& p & = & \textrm{the number of facilities to be sited} \\
&& a_i & = & \textrm{service load or population demand at client location } i \\
&& k_{i} & = & \textrm{the } k \textrm{nearest facilities of client location } i \\
&& c_{j} & = & \textrm{the capacity of facility} j \\
&& c_{j} & = & \textrm{the capacity of facility} j \\
&& d_{ij} & = & \textrm{shortest distance or travel time between locations } i \textrm{ and } j \\
&& X_{ij} & = & \begin{cases}
1, \textrm{if client location } i \textrm{ is served by facility } j \\
Expand All @@ -573,7 +574,7 @@ class KNearestPMedian(PMedian):
&& Y_j & = & \begin{cases}
1, \textrm{if a facility is sited at location } j \\
0, \textrm{otherwise} \\
\end{cases} \\
\end{cases} \\
&& g_i & = & \begin{cases}
1, \textrm{if the client } i \textrm{ needs to be served by non-k-nearest facilities} \\
0, \textrm{otherwise} \\
Expand Down Expand Up @@ -604,7 +605,7 @@ class KNearestPMedian(PMedian):
distance_metric : str
The distance metric used for computing distances between clients
and facilities.
Attributes
----------
Expand All @@ -626,11 +627,11 @@ class KNearestPMedian(PMedian):
The inverse of ``fac2cli`` where client to facility relationships
are shown.
""" # noqa
""" # noqa: E501

def __init__(
self,
weights_sum: Union[int, float],
weights_sum: int | float,
clients: np.array,
facilities: np.array,
weights: np.array,
Expand Down
4 changes: 1 addition & 3 deletions spopt/locate/util.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from typing import Union

import geopandas
import numpy
from shapely.geometry import MultiPolygon, Point, Polygon


def simulated_geo_points(
in_data: Union[geopandas.GeoDataFrame, geopandas.GeoSeries, Polygon, MultiPolygon],
in_data: geopandas.GeoDataFrame | geopandas.GeoSeries | Polygon | MultiPolygon,
needed: int = 1,
seed: int = 0,
) -> geopandas.GeoDataFrame:
Expand Down
24 changes: 13 additions & 11 deletions spopt/region/azp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
Environment and Planning A, 27(3):425-446.
"""

# ruff: noqa: B008, N806

import abc
import math
import random
Expand Down Expand Up @@ -235,7 +237,7 @@ def fit_from_scipy_sparse_matrix(
The objective function to use. Default is
``ObjectiveFunctionPairwise()``.
""" # noqa E501
""" # noqa: E501

if attr.ndim == 1:
attr = attr.reshape(adj.shape[0], -1)
Expand Down Expand Up @@ -301,7 +303,7 @@ def fit_from_w(
``fit_from_scipy_sparse_matrix``.
Default is ``ObjectiveFunctionPairwise()``.
""" # noqa E501
""" # noqa: E501

adj = scipy_sparse_matrix_from_w(w)
self.fit_from_scipy_sparse_matrix(
Expand Down Expand Up @@ -352,7 +354,7 @@ def fit_from_networkx(
``fit_from_scipy_sparse_matrix``.
Default is ``ObjectiveFunctionPairwise()``.
""" # noqa E501
""" # noqa: E501

adj = nx.to_scipy_sparse_matrix(graph)
attr = array_from_graph_or_dict(graph, attr)
Expand Down Expand Up @@ -403,7 +405,7 @@ def fit_from_geodataframe(
``fit_from_scipy_sparse_matrix``.
Default is ``ObjectiveFunctionPairwise()``.
""" # noqa E501
""" # noqa: E501
w = w_from_gdf(gdf, contiguity)
attr = array_from_df_col(gdf, attr)
self.fit_from_w(
Expand Down Expand Up @@ -442,7 +444,7 @@ def fit_from_dict(
Refer to the corresponding argument in
:meth:`fit_from_scipy_sparse_matrix`.
""" # noqa E501
""" # noqa: E501
sorted_areas = sorted(neighbor_dict)

adj = scipy_sparse_matrix_from_dict(neighbor_dict)
Expand All @@ -456,7 +458,7 @@ def fit_from_dict(
adj, attr_arr, n_regions, initial_labels, objective_func=objective_func
)

def _azp_connected_component(self, adj, initial_clustering, attr): # noqa ARG002
def _azp_connected_component(self, adj, initial_clustering, attr): # noqa: ARG002
"""
Implementation of the AZP algorithm for a spatially connected set of
areas (i.e. for every area there is a path to every other area).
Expand Down Expand Up @@ -682,7 +684,7 @@ def fit_from_geodataframe(
``fit_from_scipy_sparse_matrix``.
Default is ``ObjectiveFunctionPairwise()``.
""" # noqa E501
""" # noqa: E501
w = w_from_gdf(gdf, contiguity)
attr = array_from_df_col(gdf, attr)
self.fit_from_w(
Expand Down Expand Up @@ -726,7 +728,7 @@ def fit_from_dict(
``fit_from_scipy_sparse_matrix``.
Default is ``ObjectiveFunctionPairwise()``.
""" # noqa E501
""" # noqa: E501
sorted_areas = sorted(neighbor_dict)
adj = scipy_sparse_matrix_from_dict(neighbor_dict)
attr_arr = array_from_dict_values(attr, sorted_areas)
Expand Down Expand Up @@ -779,7 +781,7 @@ def fit_from_networkx(
``AZP.fit_from_networkx``.
Default is ``ObjectiveFunctionPairwise()``.
""" # noqa E501
""" # noqa: E501

adj = nx.to_scipy_sparse_matrix(graph)
attr = array_from_graph_or_dict(graph, attr)
Expand Down Expand Up @@ -828,7 +830,7 @@ def fit_from_scipy_sparse_matrix(
Refer to the corresponding argument in
``AZP.fit_from_scipy_sparse_matrix``.
""" # noqa E501
""" # noqa: E501
if not (0 < cooling_factor < 1):
raise ValueError(
"The cooling_factor argument must be greater than 0 and less than 1"
Expand Down Expand Up @@ -914,7 +916,7 @@ def fit_from_w(
``fit_from_scipy_sparse_matrix``.
Default is ``ObjectiveFunctionPairwise()``.
""" # noqa E501
""" # noqa: E501
adj = scipy_sparse_matrix_from_w(w)
self.fit_from_scipy_sparse_matrix(
adj,
Expand Down
2 changes: 1 addition & 1 deletion spopt/region/azp_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

class AllowMoveStrategy(abc.ABC):
def start_new_component(
self, initial_labels, attr, objective_func, comp_idx # noqa ARG002
self, initial_labels, attr, objective_func, comp_idx # noqa: ARG002
):
"""
This method should be called whenever a new connected component is
Expand Down
Loading

0 comments on commit 6c89c4f

Please sign in to comment.