# Complete Optimization Problem and Solution: allergy_1

## 1. Problem Context and Goals

### Context  
The university is focused on efficiently assigning students to dormitories while minimizing the discomfort of students with allergies. The primary decision involves determining whether a student is assigned to a specific dormitory, represented by binary variables. The objective is to minimize the total penalty incurred from assigning students with allergies to dormitories that are not allergy-friendly. This is achieved by considering the penalty values associated with such assignments. The operational parameters include the maximum number of students each dormitory can accommodate, which serves as a constraint in the optimization model. The business logic involves calculating penalties based on the assignment of students with allergies to non-allergy-friendly dormitories, ensuring that the optimization process focuses on minimizing these penalties. The data reflects current operational conditions, emphasizing linear relationships without involving complex mathematical operations like variable products or divisions. The business configuration includes both scalar parameters, such as dormitory capacity limits, and business logic formulas, ensuring a comprehensive approach to decision-making.

### Goals  
The primary goal of the optimization is to minimize the total penalty associated with assigning students with allergies to non-allergy-friendly dormitories. This involves reducing the sum of penalty values linked to such assignments. Success is measured by the extent to which the optimization reduces these penalties, aligning with the expected sources of coefficients. The objective is clearly defined in linear terms, focusing on minimizing the total penalty without involving complex mathematical expressions.

## 2. Constraints    

The optimization problem is subject to several constraints that ensure feasible and effective student assignments:

- Each student must be assigned to exactly one dormitory. This ensures that every student has a designated living space.
- The number of students assigned to a dormitory cannot exceed its capacity. This constraint respects the physical limitations of each dormitory.
- Students with allergies cannot be assigned to dormitories that are not allergy-friendly. This constraint prevents assignments that would incur penalties, aligning with the objective of minimizing discomfort for students with allergies.

These constraints are expressed in business terms that naturally lead to linear mathematical forms, avoiding any complex operations.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 2 Database Schema
-- Objective: Schema changes include creating a table for decision variables and updating configuration logic for scalar parameters and formulas based on OR expert mapping analysis.

CREATE TABLE dormitory_capacity (
  dormitory_id INTEGER,
  capacity INTEGER
);

CREATE TABLE dormitory_allergy_friendly (
  dormitory_id INTEGER,
  is_allergy_friendly BOOLEAN
);

CREATE TABLE allergy_penalty (
  student_id INTEGER,
  dormitory_id INTEGER,
  penalty_value FLOAT
);

CREATE TABLE student_dormitory_assignment (
  student_id INTEGER,
  dormitory_id INTEGER,
  assignment BOOLEAN
);


```

### Data Dictionary  
The data dictionary provides a comprehensive overview of the tables and columns, highlighting their business purposes and roles in the optimization process:

- **Dormitory Capacity**: This table records the maximum number of students each dormitory can accommodate, serving as a constraint in the optimization model.
  - **dormitory_id**: Identifies each dormitory uniquely, linking capacity information to specific dormitories.
  - **capacity**: Represents the maximum student capacity for each dormitory, used as a constraint bound.

- **Dormitory Allergy-Friendly Status**: This table indicates whether each dormitory is suitable for students with allergies.
  - **dormitory_id**: Uniquely identifies each dormitory, linking allergy-friendliness information.
  - **is_allergy_friendly**: Shows whether a dormitory is allergy-friendly, influencing valid student assignments.

- **Allergy Penalty**: This table stores penalty values for assigning students with allergies to non-allergy-friendly dormitories.
  - **student_id**: Uniquely identifies each student, linking penalty information.
  - **dormitory_id**: Uniquely identifies each dormitory, linking penalty information.
  - **penalty_value**: Represents the penalty for assigning a student with allergies to a non-allergy-friendly dormitory, used in the objective function.

- **Student Dormitory Assignment**: This table records the assignment of students to dormitories as binary decision variables.
  - **student_id**: Uniquely identifies each student, linking assignment information.
  - **dormitory_id**: Uniquely identifies each dormitory, linking assignment information.
  - **assignment**: Indicates whether a student is assigned to a dormitory, used as a decision variable in the optimization.

### Current Stored Values  
```sql
-- Iteration 2 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical university dormitory capacities, common allergy considerations, and realistic penalty values to ensure a meaningful optimization problem.

-- Realistic data for dormitory_capacity
INSERT INTO dormitory_capacity (dormitory_id, capacity) VALUES (1, 50);
INSERT INTO dormitory_capacity (dormitory_id, capacity) VALUES (2, 100);
INSERT INTO dormitory_capacity (dormitory_id, capacity) VALUES (3, 150);

-- Realistic data for dormitory_allergy_friendly
INSERT INTO dormitory_allergy_friendly (dormitory_id, is_allergy_friendly) VALUES (1, True);
INSERT INTO dormitory_allergy_friendly (dormitory_id, is_allergy_friendly) VALUES (2, False);
INSERT INTO dormitory_allergy_friendly (dormitory_id, is_allergy_friendly) VALUES (3, True);

-- Realistic data for allergy_penalty
INSERT INTO allergy_penalty (student_id, dormitory_id, penalty_value) VALUES (101, 2, 20.0);
INSERT INTO allergy_penalty (student_id, dormitory_id, penalty_value) VALUES (102, 2, 25.0);
INSERT INTO allergy_penalty (student_id, dormitory_id, penalty_value) VALUES (103, 2, 15.0);

-- Realistic data for student_dormitory_assignment
INSERT INTO student_dormitory_assignment (student_id, dormitory_id, assignment) VALUES (101, 1, True);
INSERT INTO student_dormitory_assignment (student_id, dormitory_id, assignment) VALUES (102, 3, True);
INSERT INTO student_dormitory_assignment (student_id, dormitory_id, assignment) VALUES (103, 2, False);


```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_{ij} \) be a binary decision variable where \( x_{ij} = 1 \) if student \( i \) is assigned to dormitory \( j \), and \( x_{ij} = 0 \) otherwise.

#### Objective Function
Minimize the total penalty for assigning students with allergies to non-allergy-friendly dormitories:

\[
\text{Minimize } \sum_{i,j} \text{penalty\_value}_{ij} \times x_{ij}
\]

where \(\text{penalty\_value}_{ij}\) is the penalty for assigning student \( i \) to dormitory \( j \) from the `allergy_penalty` table.

#### Constraints
1. **Each student must be assigned to exactly one dormitory:**

   \[
   \sum_{j} x_{ij} = 1 \quad \forall i
   \]

2. **The number of students assigned to a dormitory cannot exceed its capacity:**

   \[
   \sum_{i} x_{ij} \leq \text{capacity}_j \quad \forall j
   \]

   where \(\text{capacity}_j\) is the capacity of dormitory \( j \) from the `dormitory_capacity` table.

3. **Students with allergies cannot be assigned to non-allergy-friendly dormitories:**

   \[
   x_{ij} = 0 \quad \forall i, j \text{ where student } i \text{ has allergies and dormitory } j \text{ is not allergy-friendly}
   \]

   This constraint is enforced by setting \( x_{ij} = 0 \) for all combinations of \( i \) and \( j \) where the dormitory is not allergy-friendly, as indicated by the `dormitory_allergy_friendly` table.

Data Source Verification:
- **penalty_value**: `allergy_penalty.penalty_value`
- **capacity**: `dormitory_capacity.capacity`
- **allergy-friendly status**: `dormitory_allergy_friendly.is_allergy_friendly`

This formulation provides a complete and immediately solvable linear mathematical model, ensuring that all coefficients are derived from the provided data.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_dormitory_assignment():
    """Optimize student dormitory assignments to minimize allergy penalties."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("allergy_1")
    
    # Data from the database schema
    dormitory_capacity = {1: 50, 2: 100, 3: 150}
    dormitory_allergy_friendly = {1: True, 2: False, 3: True}
    allergy_penalty = {
        (101, 2): 20.0,
        (102, 2): 25.0,
        (103, 2): 15.0
    }
    
    students = [101, 102, 103]
    dormitories = [1, 2, 3]
    
    # CRITICAL: Validate array lengths before loops
    assert len(dormitory_capacity) == len(dormitory_allergy_friendly) == len(dormitories), "Array length mismatch"
    
    # 2. VARIABLES
    # Binary decision variables for student-dormitory assignments
    x = model.addVars(students, dormitories, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the total penalty for assigning students with allergies to non-allergy-friendly dormitories
    model.setObjective(
        gp.quicksum(allergy_penalty.get((i, j), 0) * x[i, j] for i in students for j in dormitories),
        GRB.MINIMIZE
    )
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Each student must be assigned to exactly one dormitory
    model.addConstrs(
        (gp.quicksum(x[i, j] for j in dormitories) == 1 for i in students),
        name="student_assignment"
    )
    
    # The number of students assigned to a dormitory cannot exceed its capacity
    model.addConstrs(
        (gp.quicksum(x[i, j] for i in students) <= dormitory_capacity[j] for j in dormitories),
        name="dormitory_capacity"
    )
    
    # Students with allergies cannot be assigned to non-allergy-friendly dormitories
    model.addConstrs(
        (x[i, j] == 0 for i in students for j in dormitories if not dormitory_allergy_friendly[j] and (i, j) in allergy_penalty),
        name="allergy_constraint"
    )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in students:
            for j in dormitories:
                if x[i, j].x > 1e-6:
                    print(f"Student {i} assigned to Dormitory {j}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_dormitory_assignment()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 0.0
**Execution Time**: 0.18 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution with an objective value of 0.0, indicating no penalties. The execution time was the fastest among the solvers, suggesting efficient performance.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_student_dormitory_assignment():
    # 1. MODEL & DATA SETUP
    mdl = Model(name="allergy_1")
    
    # Data from the database schema
    dormitory_capacity = {1: 50, 2: 100, 3: 150}
    dormitory_allergy_friendly = {1: True, 2: False, 3: True}
    allergy_penalty = {
        (101, 2): 20.0,
        (102, 2): 25.0,
        (103, 2): 15.0
    }
    
    students = {101, 102, 103}
    dormitories = {1, 2, 3}
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(dormitory_capacity) == len(dormitory_allergy_friendly) == len(dormitories), "Array length mismatch"
    
    # 2. VARIABLES
    x = {(i, j): mdl.binary_var(name=f"x_{i}_{j}") for i in students for j in dormitories}
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum(allergy_penalty.get((i, j), 0) * x[i, j] for i in students for j in dormitories)
    mdl.minimize(objective)
    
    # 4. CONSTRAINTS
    
    # Each student must be assigned to exactly one dormitory
    for i in students:
        mdl.add_constraint(mdl.sum(x[i, j] for j in dormitories) == 1, ctname=f"assign_{i}")
    
    # The number of students assigned to a dormitory cannot exceed its capacity
    for j in dormitories:
        mdl.add_constraint(mdl.sum(x[i, j] for i in students) <= dormitory_capacity[j], ctname=f"capacity_{j}")
    
    # Students with allergies cannot be assigned to non-allergy-friendly dormitories
    for i in students:
        for j in dormitories:
            if not dormitory_allergy_friendly[j]:
                mdl.add_constraint(x[i, j] == 0, ctname=f"allergy_{i}_{j}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in students:
            for j in dormitories:
                if solution.get_value(x[i, j]) > 1e-6:
                    print(f"Student {i} assigned to Dormitory {j}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")

    return mdl

# Run the optimization
optimize_student_dormitory_assignment()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 0.0
**Execution Time**: 1.62 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution with an objective value of 0.0. Although the execution time was longer than Gurobipy, the result is consistent and reliable.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

import pyomo.environ as pyo
from pyomo.opt import SolverFactory

def dormitory_assignment_optimization():
    """Optimize student dormitory assignments to minimize allergy penalties."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Example data based on the provided problem context
    students = [101, 102, 103]
    dormitories = [1, 2, 3]
    
    # Penalty values for assigning students to dormitories
    penalty_values = {
        (101, 2): 20.0,
        (102, 2): 25.0,
        (103, 2): 15.0
    }
    
    # Dormitory capacities
    capacities = {
        1: 50,
        2: 100,
        3: 150
    }
    
    # Allergy-friendly status of dormitories
    allergy_friendly = {
        1: True,
        2: False,
        3: True
    }
    
    # 3. SETS
    model.I = pyo.Set(initialize=students)
    model.J = pyo.Set(initialize=dormitories)
    
    # 4. PARAMETERS
    model.penalty_value = pyo.Param(model.I, model.J, initialize=penalty_values, default=0.0)
    model.capacity = pyo.Param(model.J, initialize=capacities)
    model.is_allergy_friendly = pyo.Param(model.J, initialize=allergy_friendly)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.I, model.J, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.penalty_value[i, j] * model.x[i, j] for i in model.I for j in model.J)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.minimize)
    
    # 7. CONSTRAINTS
    
    # Each student must be assigned to exactly one dormitory
    def student_assignment_rule(model, i):
        return sum(model.x[i, j] for j in model.J) == 1
    model.student_assignment_constraint = pyo.Constraint(model.I, rule=student_assignment_rule)
    
    # The number of students assigned to a dormitory cannot exceed its capacity
    def capacity_rule(model, j):
        return sum(model.x[i, j] for i in model.I) <= model.capacity[j]
    model.capacity_constraint = pyo.Constraint(model.J, rule=capacity_rule)
    
    # Students with allergies cannot be assigned to non-allergy-friendly dormitories
    def allergy_constraint_rule(model, i, j):
        if not model.is_allergy_friendly[j]:
            return model.x[i, j] == 0
        return pyo.Constraint.Skip
    model.allergy_constraint = pyo.Constraint(model.I, model.J, rule=allergy_constraint_rule)
    
    # 8. SOLVING WITH GUROBI
    solver = SolverFactory('gurobi')
    
    # Optional: Set solver options
    solver.options['TimeLimit'] = 300  # 5 minutes
    solver.options['MIPGap'] = 0.01    # 1% gap
    
    # Solve the model
    results = solver.solve(model, tee=True)  # tee=True shows solver output
    
    # 9. RESULT PROCESSING
    # Check solver status
    if results.solver.termination_condition == pyo.TerminationCondition.optimal:
        print("Optimal solution found!")
        print(f"Optimal value: {pyo.value(model.objective)}")
        
        # Extract variable values
        print("\nAssignment values:")
        for i in model.I:
            for j in model.J:
                x_val = pyo.value(model.x[i, j])
                if x_val > 1e-6:  # Only print non-zero values
                    print(f"Student {i} assigned to Dormitory {j}: {int(x_val)}")
        
    elif results.solver.termination_condition == pyo.TerminationCondition.infeasible:
        print("Problem is infeasible")
    elif results.solver.termination_condition == pyo.TerminationCondition.unbounded:
        print("Problem is unbounded")
    else:
        print(f"Solver terminated with condition: {results.solver.termination_condition}")
    
    return model

# Run the optimization
dormitory_assignment_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 0.0
**Execution Time**: 1.11 seconds
**Reliability**: high
**Analysis**: Pyomo achieved an optimal solution with an objective value of 0.0. The execution time was between Gurobipy and DOCplex, and the results are consistent with the other solvers.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 0.00 | 0.18s | N/A | N/A |
| Docplex | OPTIMAL | 0.00 | 1.62s | N/A | N/A |
| Pyomo | OPTIMAL | 0.00 | 1.11s | N/A | N/A |

### Solver Consistency Analysis
**Result**: All solvers produced consistent results ✓
**Consistent Solvers**: gurobipy, docplex, pyomo
**Majority Vote Optimal Value**: 0.0

### Final Recommendation
**Recommended Optimal Value**: 0.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is recommended due to its fastest execution time and consistent results with other solvers.

### Business Interpretation
**Overall Strategy**: The optimal solution indicates that all students with allergies are assigned to allergy-friendly dormitories, resulting in no penalties.
**Objective Value Meaning**: An objective value of 0.0 means that all students with allergies are appropriately assigned to allergy-friendly dormitories, avoiding any penalties.
**Resource Allocation Summary**: All dormitory assignments are made within capacity limits and allergy constraints, ensuring optimal resource utilization.
**Implementation Recommendations**: Implement the solution by assigning students to dormitories as per the solver's output, ensuring compliance with allergy and capacity constraints.