| name | set-covering-problem |
| description | When the user wants to solve set covering problems, determine minimum coverage sets, or optimize facility coverage. Also use when the user mentions "set cover," "minimum set cover," "coverage optimization," "facility coverage problem," "service coverage," "location set covering," "maximal covering location problem," or "covering design." For general facility location, see facility-location-problem. For specific applications, see warehouse-location-optimization or hub-location-problem. |
Set Covering Problem
You are an expert in set covering problems and coverage-based optimization. Your goal is to help find the minimum cost collection of sets (or facilities) that covers all required elements (or customers), commonly used for facility location, service coverage, and resource allocation problems.
Initial Assessment
Before solving set covering problems, understand:
-
Problem Type
- Set Covering Problem (SCP)? (cover all elements with minimum cost)
- Maximal Covering Location Problem (MCLP)? (maximize covered demand with limited resources)
- Location Set Covering Problem (LSCP)? (minimum facilities for full coverage)
- Partial Set Covering? (cover a percentage of elements)
- Redundant Coverage? (elements covered multiple times)
-
Coverage Requirements
- Must cover all elements? (100% coverage)
- Partial coverage acceptable? (e.g., 95%)
- Coverage distance/time threshold?
- Redundancy requirements? (backup coverage)
- Quality of coverage (single vs. multiple cover)?
-
Elements to Cover
- What needs to be covered? (customers, demand points, areas)
- How many elements?
- Weights/priorities for elements?
- Geographic locations?
- Time-dependent coverage needs?
-
Coverage Sets/Facilities
- How many potential covering sets/facilities?
- Cost structure? (fixed costs, variable costs)
- Coverage radius or service area?
- Capacity constraints?
- Can sets/facilities overlap in coverage?
-
Objectives
- Minimize number of facilities?
- Minimize total cost?
- Maximize covered demand (with budget)?
- Balance coverage and cost?
- Ensure redundancy for reliability?
Set Covering Problem Framework
Problem Variants
1. Basic Set Covering Problem (SCP)
- Goal: Cover all elements with minimum cost
- Constraint: Every element must be covered at least once
- Application: Emergency service location, sensor placement
2. Location Set Covering Problem (LSCP)
- Goal: Minimize number of facilities for full coverage
- Constraint: All demand points within coverage distance
- Application: Fire station, ambulance location
3. Maximal Covering Location Problem (MCLP)
- Goal: Maximize demand covered with limited facilities
- Constraint: Can only open p facilities
- Application: Retail location with budget constraint
4. Partial Set Covering
- Goal: Minimize cost while covering at least α% of elements
- Constraint: α ≤ coverage ≤ 100%
- Application: Cost-effective service coverage
5. Redundant Coverage (Backup Coverage)
- Goal: Ensure elements covered by multiple facilities
- Constraint: Each element covered by at least k facilities
- Application: Reliable emergency response, fault-tolerant networks
Mathematical Formulations
Basic Set Covering Problem (SCP)
Sets:
- I = {1, ..., n}: Set of elements to be covered
- J = {1, ..., m}: Set of potential covering sets/facilities
Parameters:
- c_j: Cost of selecting set/facility j
- a_{ij}: Coverage coefficient (1 if set j covers element i, 0 otherwise)
Decision Variables:
- x_j ∈ {0,1}: 1 if set j is selected, 0 otherwise
Objective Function:
Minimize: Σ_{j=1}^m c_j × x_j
Constraints:
1. Coverage: Every element must be covered
Σ_{j:a_{ij}=1} x_j ≥ 1, ∀i ∈ I
Or equivalently:
Σ_{j=1}^m a_{ij} × x_j ≥ 1, ∀i ∈ I
2. Binary variables:
x_j ∈ {0,1}, ∀j ∈ J
Complexity: NP-complete
Location Set Covering Problem (LSCP)
Parameters:
- d_{ij}: Distance from facility site j to demand point i
- S: Maximum service distance (coverage radius)
Coverage Matrix:
a_{ij} = 1 if d_{ij} ≤ S
a_{ij} = 0 otherwise
Objective:
Minimize: Σ_{j=1}^m x_j (minimize number of facilities)
Constraints:
Same coverage constraints as SCP
Maximal Covering Location Problem (MCLP)
Additional Parameters:
- w_i: Weight/importance of demand point i (e.g., population, demand)
- p: Number of facilities to locate (budget constraint)
Decision Variables:
- x_j ∈ {0,1}: 1 if facility j is opened
- y_i ∈ {0,1}: 1 if demand point i is covered
Objective Function:
Maximize: Σ_{i=1}^n w_i × y_i (maximize covered demand)
Constraints:
1. Coverage definition:
y_i ≤ Σ_{j:a_{ij}=1} x_j, ∀i ∈ I
2. Facility limit:
Σ_{j=1}^m x_j ≤ p
3. Binary variables:
x_j, y_i ∈ {0,1}
Redundant Coverage (k-Coverage)
Constraint:
Each element must be covered by at least k facilities:
Σ_{j:a_{ij}=1} x_j ≥ k, ∀i ∈ I
Exact Solution Methods
1. Set Covering Problem with PuLP
from pulp import *
import numpy as np
def solve_set_covering(costs, coverage_matrix, element_names=None,
set_names=None, redundancy=1):
"""
Solve Set Covering Problem
Args:
costs: list of costs for each set/facility
coverage_matrix: binary matrix [elements x sets]
coverage_matrix[i][j] = 1 if set j covers element i
element_names: optional element names
set_names: optional set names
redundancy: coverage redundancy (k-coverage), default 1
Returns:
optimal solution
"""
n_elements = len(coverage_matrix)
n_sets = len(coverage_matrix[0]) if n_elements > 0 else 0
if element_names is None:
element_names = [f"Element_{i}" for i in range(n_elements)]
if set_names is None:
set_names = [f"Set_{j}" for j in range(n_sets)]
prob = LpProblem("Set_Covering", LpMinimize)
x = LpVariable.dicts("select", range(n_sets), cat='Binary')
prob += lpSum([costs[j] * x[j] for j in (n_sets)]),
i (n_elements):
prob += (
lpSum([coverage_matrix[i][j] * x[j] j (n_sets)]) >= redundancy,
)
time
start_time = time.time()
prob.solve(PULP_CBC_CMD(msg=, timeLimit=))
solve_time = time.time() - start_time
LpStatus[prob.status] [, ]:
selected_sets = [j j (n_sets) x[j].varValue > ]
element_coverage = {}
i (n_elements):
covering_sets = [j j selected_sets
coverage_matrix[i][j] == ]
element_coverage[i] = covering_sets
{
: LpStatus[prob.status],
: value(prob.objective),
: (selected_sets),
: selected_sets,
: [set_names[j] j selected_sets],
: element_coverage,
: solve_time,
: redundancy
}
:
{
: LpStatus[prob.status],
: solve_time
}
__name__ == :
costs = [, , , , , , , ]
coverage_matrix = [
[, , , , , , , ],
[, , , , , , , ],
[, , , , , , , ],
[, , , , , , , ],
[, , , , , , , ],
[, , , , , , , ],
[, , , , , , , ],
[, , , , , , , ],
[, , , , , , , ],
[, , , , , , , ],
[, , , , , , , ],
[, , , , , , , ],
]
demand_names = [ i ()]
facility_names = [ i ()]
(*)
()
(*)
()
()
result = solve_set_covering(costs, coverage_matrix,
demand_names, facility_names,
redundancy=)
()
()
()
()
()
()
()
()
()
i, covering_facilities result[].items():
()
()
()
()
()
result_redundant = solve_set_covering(costs, coverage_matrix,
demand_names, facility_names,
redundancy=)
()
()
()
()
()
i, covering_facilities result_redundant[].items():
coverage_count = (covering_facilities)
status = coverage_count >=
(
)
2. Location Set Covering Problem (LSCP)
def solve_location_set_covering(facility_coords, demand_coords,
service_radius, facility_costs=None):
"""
Solve Location Set Covering Problem
Minimize number of facilities to cover all demand within service radius
Args:
facility_coords: array of potential facility coordinates
demand_coords: array of demand point coordinates
service_radius: maximum service distance
facility_costs: optional costs (if None, minimize count)
Returns:
optimal facility locations
"""
n_facilities = len(facility_coords)
n_demands = len(demand_coords)
coverage_matrix = np.zeros((n_demands, n_facilities))
for i in range(n_demands):
for j in range(n_facilities):
distance = np.linalg.norm(demand_coords[i] - facility_coords[j])
if distance <= service_radius:
coverage_matrix[i][j] = 1
if facility_costs is None:
facility_costs = [1] * n_facilities
result = solve_set_covering(facility_costs, coverage_matrix)
if result['status'] in ['Optimal', 'Feasible']:
demand_distances = {}
for i in range(n_demands):
covering_facilities = result['element_coverage'][i]
if covering_facilities:
distances = [
np.linalg.norm(demand_coords[i] - facility_coords[j])
j covering_facilities
]
demand_distances[i] = {
: (distances),
: covering_facilities
}
result[] = demand_distances
result[] = service_radius
result
np.random.seed()
n_facilities =
n_demands =
facility_coords = np.random.rand(n_facilities, ) *
demand_coords = np.random.rand(n_demands, ) *
service_radius =
( + *)
()
(*)
()
()
()
result = solve_location_set_covering(facility_coords, demand_coords,
service_radius)
()
()
()
()
()
()
()
all_min_distances = [d[] d result[].values()]
()
()
()
3. Maximal Covering Location Problem (MCLP)
def solve_maximal_covering(facility_coords, demand_coords, demand_weights,
service_radius, max_facilities):
"""
Solve Maximal Covering Location Problem
Maximize covered demand with limited number of facilities
Args:
facility_coords: potential facility coordinates
demand_coords: demand point coordinates
demand_weights: demand weights (population, demand volume, etc.)
service_radius: coverage radius
max_facilities: maximum number of facilities to open
Returns:
optimal solution maximizing covered demand
"""
n_facilities = len(facility_coords)
n_demands = len(demand_coords)
coverage_matrix = np.zeros((n_demands, n_facilities))
for i in range(n_demands):
for j in range(n_facilities):
distance = np.linalg.norm(demand_coords[i] - facility_coords[j])
if distance <= service_radius:
coverage_matrix[i][j] = 1
prob = LpProblem("Maximal_Covering", LpMaximize)
x = LpVariable.dicts("facility", range(n_facilities), cat='Binary')
y = LpVariable.dicts("covered", range(n_demands), cat='Binary')
prob += (
lpSum([demand_weights[i] * y[i] for i in range(n_demands)]),
"Total_Covered_Demand"
)
for i in range(n_demands):
prob += (
y[i] <= lpSum([coverage_matrix[i][j] * x[j]
j (n_facilities)]),
)
prob += (
lpSum([x[j] j (n_facilities)]) <= max_facilities,
)
time
start_time = time.time()
prob.solve(PULP_CBC_CMD(msg=, timeLimit=))
solve_time = time.time() - start_time
LpStatus[prob.status] [, ]:
open_facilities = [j j (n_facilities)
x[j].varValue > ]
covered_demands = [i i (n_demands)
y[i].varValue > ]
uncovered_demands = [i i (n_demands)
y[i].varValue < ]
total_demand = (demand_weights)
covered_demand = (demand_weights[i] i covered_demands)
coverage_percentage = (covered_demand / total_demand) *
{
: LpStatus[prob.status],
: covered_demand,
: total_demand,
: coverage_percentage,
: (open_facilities),
: max_facilities,
: open_facilities,
: covered_demands,
: uncovered_demands,
: solve_time
}
:
{
: LpStatus[prob.status],
: solve_time
}
demand_weights = np.random.uniform(, , n_demands)
max_facilities =
( + *)
()
(*)
()
()
()
()
()
result = solve_maximal_covering(facility_coords, demand_coords,
demand_weights, service_radius,
max_facilities)
()
()
()
()
()
()
()
()
()
()
result[]:
()
i result[][:]:
()
Greedy Heuristics
1. Greedy Set Covering
def greedy_set_covering(costs, coverage_matrix):
"""
Greedy heuristic for set covering
Iteratively select set with best cost-effectiveness ratio
Args:
costs: set costs
coverage_matrix: coverage matrix
Returns:
heuristic solution
"""
n_elements = len(coverage_matrix)
n_sets = len(coverage_matrix[0])
uncovered_elements = set(range(n_elements))
selected_sets = []
total_cost = 0
while uncovered_elements:
best_set = None
best_ratio = float('inf')
for j in range(n_sets):
if j in selected_sets:
continue
newly_covered = sum(1 for i in uncovered_elements
if coverage_matrix[i][j] == 1)
if newly_covered > 0:
ratio = costs[j] / newly_covered
if ratio < best_ratio:
best_ratio = ratio
best_set = j
if best_set is None:
break
selected_sets.append(best_set)
total_cost += costs[best_set]
newly_covered_elements = {i i uncovered_elements
coverage_matrix[i][best_set] == }
uncovered_elements -= newly_covered_elements
{
: selected_sets,
: (selected_sets),
: total_cost,
: (uncovered_elements) == ,
:
}
2. Greedy Location for LSCP
def greedy_location_covering(facility_coords, demand_coords, service_radius):
"""
Greedy heuristic for location set covering
Iteratively select facility that covers most uncovered demands
Args:
facility_coords: facility coordinates
demand_coords: demand coordinates
service_radius: service radius
Returns:
heuristic facility selection
"""
n_facilities = len(facility_coords)
n_demands = len(demand_coords)
coverage = {}
for j in range(n_facilities):
coverage[j] = set()
for i in range(n_demands):
distance = np.linalg.norm(demand_coords[i] - facility_coords[j])
if distance <= service_radius:
coverage[j].add(i)
uncovered_demands = set(range(n_demands))
selected_facilities = []
while uncovered_demands:
best_facility = None
max_new_coverage = 0
for j in range(n_facilities):
if j in selected_facilities:
continue
new_coverage = len(coverage[j] & uncovered_demands)
if new_coverage > max_new_coverage:
max_new_coverage = new_coverage
best_facility = j
if best_facility is None:
break
selected_facilities.append(best_facility)
uncovered_demands -= coverage[best_facility]
return {
: selected_facilities,
: (selected_facilities),
: (uncovered_demands) == ,
: (uncovered_demands),
:
}
Complete Set Covering Solver
class SetCoveringSolver:
"""
Comprehensive Set Covering Problem Solver
"""
def __init__(self):
self.problem_type = None
self.loaded = False
def load_set_covering(self, costs, coverage_matrix,
element_names=None, set_names=None):
"""Load basic set covering problem"""
self.costs = np.array(costs)
self.coverage_matrix = np.array(coverage_matrix)
self.element_names = element_names
self.set_names = set_names
self.problem_type = 'SCP'
self.loaded = True
print(f"Loaded Set Covering Problem:")
print(f" Elements: {len(coverage_matrix)}")
print(f" Sets: {len(costs)}")
def load_location_covering(self, facility_coords, demand_coords,
service_radius, demand_weights=None):
"""Load location-based covering problem"""
self.facility_coords = np.array(facility_coords)
self.demand_coords = np.array(demand_coords)
self.service_radius = service_radius
.demand_weights = demand_weights
.problem_type =
.loaded =
()
()
()
()
():
.loaded:
ValueError()
.problem_type == :
solve_set_covering(.costs, .coverage_matrix,
.element_names, .set_names,
redundancy)
.problem_type == :
max_facilities:
weights = .demand_weights .demand_weights \
np.ones((.demand_coords))
solve_maximal_covering(
.facility_coords, .demand_coords,
weights, .service_radius, max_facilities
)
:
solve_location_set_covering(
.facility_coords, .demand_coords,
.service_radius
)
():
.loaded:
ValueError()
method == :
.problem_type == :
greedy_set_covering(.costs, .coverage_matrix)
.problem_type == :
greedy_location_covering(
.facility_coords, .demand_coords,
.service_radius
)
():
pandas pd
results = []
method methods:
time
start = time.time()
:
method == :
sol = .solve_exact()
:
sol = .solve_heuristic(method)
solve_time = time.time() - start
results.append({
: method,
: sol.get() sol.get(),
: sol.get(, ),
:
})
Exception e:
()
pd.DataFrame(results)
():
.problem_type != :
()
matplotlib.pyplot plt
plt.figure(figsize=(, ))
plt.scatter(.demand_coords[:, ], .demand_coords[:, ],
c=, s=, alpha=, label=)
plt.scatter(.facility_coords[:, ], .facility_coords[:, ],
c=, s=, alpha=, marker=,
label=)
selected = solution.get() solution.get() \
solution.get()
selected:
selected_coords = .facility_coords[selected]
plt.scatter(selected_coords[:, ], selected_coords[:, ],
c=, s=, alpha=, marker=,
label=, edgecolors=, linewidths=)
idx selected:
circle = plt.Circle(
.facility_coords[idx],
.service_radius,
fill=, edgecolor=, alpha=, linewidth=
)
plt.gca().add_patch(circle)
plt.xlabel()
plt.ylabel()
plt.title()
plt.legend()
plt.grid(, alpha=)
plt.axis()
plt.tight_layout()
plt.show()
__name__ == :
(*)
()
(*)
np.random.seed()
n_facilities =
n_demands =
facility_coords = np.random.rand(n_facilities, ) *
demand_coords = np.random.rand(n_demands, ) *
service_radius =
solver = SetCoveringSolver()
solver.load_location_covering(facility_coords, demand_coords, service_radius)
( + *)
()
(*)
comparison = solver.compare_methods([, ])
( + comparison.to_string(index=))
( + *)
()
(*)
optimal = solver.solve_exact()
()
()
()
solver.visualize_coverage(optimal)
Tools & Libraries
Python:
- PuLP, Pyomo, OR-Tools
- NetworkX for graph-based coverage
- scikit-learn for clustering
Applications:
- Emergency services
- Retail location
- Sensor placement
- Network design
Common Challenges & Solutions
Large Problems: Use greedy, column generation, Lagrangian relaxation
Dynamic Coverage: Update coverage sets, re-optimize periodically
Probabilistic Demand: Stochastic models, robust optimization
Output Format
Coverage Solution:
- Sets Selected: X
- Total Cost: $Y
- Coverage: 100% (or Z%)
- Redundancy: k-coverage
Questions to Ask
- What needs to be covered?
- Coverage requirements (100%, partial)?
- Redundancy needed?
- Budget constraints?
- Coverage radius/distance?
- Costs to consider?
Related Skills
- facility-location-problem: General facility location
- warehouse-location-optimization: Warehouse coverage
- hub-location-problem: Hub coverage
- network-flow-optimization: Network-based coverage
- optimization-modeling: MIP formulation