You are in charge of a national health agency planning to establish 2 regional emergency blood centers in a province by selecting from 4 candidate cities. These centers will serve the province's 3 main hospitals. The success probability of blood transportation is known to decrease with the square of the distance, following the formula: p = 1 - 0.00005 * (distance)², where p is the success probability. Regulations require that for each hospital, the joint supply failure probability (i.e., the probability that all designated service channels fail) must be less than 10%. The agency's objective is to select the two locations that minimize the total construction cost while satisfying this reliability constraint. The costs for candidate locations, hospital locations, and distance data are shown in Table 1.

How should the agency select the two locations for the centers to minimize the total construction cost, subject to the reliability requirement?

Table 1: Blood Center Location Data
| Candidate City | Construction Cost (¥M) | Distance to H1 (km) | Distance to H2 (km) | Distance to H3 (km) |
|:---:|:---:|:---:|:---:|:---:|
| C1 | 8 | 50 | 80 | 120 |
| C2 | 7 | 60 | 40 | 90 |
| C3 | 9 | 110 | 70 | 50 |
| C4 | 7.5 | 130 | 100 | 60 |

Problem type: NLP (Small: 7 variables, 4 constraints, 7 nonzeros)

Mathematical model:
Set:
1.  CandidateCities
    The set of candidate cities, \( C = \{1, 2, 3, 4\} \)
2.  Hospitals
    The set of hospitals to be served, \( H = \{1, 2, 3\} \)

Parameter:
1.  ConstructionCost
    [8, 7, 9, 7.5] # Construction cost for each city c ∈ C in ¥M
2.  Distances
    [[50, 80, 120], [60, 40, 90], [110, 70, 50], [130, 100, 60]] # Distance from city c ∈ C to hospital h ∈ H in km
3.  NumToSelect
    2 # The number of centers to be established
4.  MaxJointFailureProb
    0.1 # The maximum allowed joint supply failure probability for each hospital
5.  ProbCoeff
    0.00005 # Coefficient for the transportation failure probability calculation

Decision variable:
1.  SelectCity
    Binary variable, \( SelectCity[c] \forall c \in C \), equals 1 if a center is built in city \(c\), 0 otherwise.

Objective:
1.  Minimize the total construction cost.
    min: \( \sum_{c \in C} ConstructionCost[c] \cdot SelectCity[c] \)

Constraint:
1.  Total number of centers constraint.
    \( \sum_{c \in C} SelectCity[c] = NumToSelect \)
2.  Reliability constraint for each hospital.
    \( \prod_{c \in C} (1 - (1 - ProbCoeff \cdot Distances[c][h]^2) \cdot SelectCity[c]) < MaxJointFailureProb \forall h \in H \)
3.  Variable domain constraints.
    \( SelectCity[c] \in \{0, 1\} \forall c \in C \)

Type:
Integer, Linear, Non-linear
NLP

Gurobi implementation:
import gurobipy as gp
from gurobipy import GRB

def solve_blood_center_location(
    candidate_cities=[1, 2, 3, 4],
    hospitals=[1, 2, 3],
    construction_cost={1: 8, 2: 7, 3: 9, 4: 7.5},
    distances_matrix=[[50, 80, 120], [60, 40, 90], [110, 70, 50], [130, 100, 60]],
    num_to_select=2,
    max_joint_failure_prob=0.1,
    prob_coeff=0.00005
):
    """
    Solve the blood center location problem using Gurobi.
    The model aims to minimize construction cost while satisfying the number of centers and supply reliability constraints.
    """
    # Create a new model
    model = gp.Model("BloodCenterLocation")

    # Convert distance data to a dictionary for easy access
    distances = {(c, h): distances_matrix[c-1][h-1]
                 for c in candidate_cities for h in hospitals}

    # --- Decision Variables ---
    # 1 if a center is built in city c, 0 otherwise
    select_city = model.addVars(candidate_cities, vtype=GRB.BINARY, name="SelectCity")
    
    # Auxiliary variable for joint failure probability for each hospital
    joint_failure_prob = model.addVars(hospitals, vtype=GRB.CONTINUOUS, 
                                       lb=0, ub=1, name="JointFailureProb")

    # --- Objective Function: Minimize Total Construction Cost ---
    total_construction_cost = gp.quicksum(construction_cost[c] * select_city[c]
                                          for c in candidate_cities)
    model.setObjective(total_construction_cost, GRB.MINIMIZE)

    # --- Constraints ---

    # 1. The total number of centers built must equal the specified number
    model.addConstr(select_city.sum('*') == num_to_select, name="TotalCentersConstraint")

    # 2. Supply reliability constraint for each hospital (nonlinear constraint)
    for h in hospitals:
        # Expression for joint failure probability
        joint_failure_prob_expr = 1
        for c in candidate_cities:
            fail_prob_ch = prob_coeff * (distances[c, h] ** 2)
            term = (1 - select_city[c]) + select_city[c] * fail_prob_ch
            joint_failure_prob_expr *= term
        
        # Use equality constraint to define auxiliary variable
        model.addConstr(joint_failure_prob[h] == joint_failure_prob_expr,
                        name=f"JointFailureProb_Hospital_{h}")
        
        # Add linear constraint for reliability requirement
        model.addConstr(joint_failure_prob[h] <= max_joint_failure_prob,
                        name=f"Reliability_Hospital_{h}")

    # --- Solve ---
    # Gurobi needs to know the model is non-convex
    model.params.NonConvex = 2
    model.optimize()

    # --- Return Results ---
    if model.status == GRB.OPTIMAL:
        return {"status": "optimal", "obj": model.objVal}
    else:
        return {"status": f"{model.status}"}


# Run the solver function
if __name__ == '__main__':
    result = solve_blood_center_location()
    print(result)

Solution status: OPTIMAL
Optimal objective value: 17