# Complete Optimization Problem and Solution: student_1

## 1. Problem Context and Goals

### Context  
A school district is tasked with assigning students to classrooms in a way that balances the workload among teachers while ensuring that no classroom exceeds its capacity. The primary decision involves determining whether a student is assigned to a specific classroom, represented as a binary choice. The operational goal is to minimize the maximum number of students in any classroom, ensuring that the distribution of students is as even as possible across all classrooms.  

The business configuration includes a predefined maximum number of students allowed in any classroom, which is used to guide the optimization process. Additionally, a business logic formula calculates the maximum number of students in any classroom by summing the assignments for each classroom and identifying the highest value. This calculation directly supports the optimization objective of minimizing the maximum number of students in any classroom.  

The problem is designed to ensure that each student is assigned to exactly one classroom and that the total number of students in each classroom does not exceed its capacity. These requirements are expressed as linear relationships, avoiding any nonlinear scenarios such as variable products or divisions.  

### Goals  
The optimization goal is to minimize the maximum number of students in any classroom. Success is measured by achieving the smallest possible value for this maximum, ensuring that classroom assignments are balanced and within capacity limits. This goal aligns with the operational parameters and business configuration, which provide the necessary data and constraints to guide the optimization process.  

## 2. Constraints  

The problem is subject to the following constraints:  
1. **Student Assignment Constraint**: Each student must be assigned to exactly one classroom. This ensures that all students are placed and no student is left unassigned.  
2. **Classroom Capacity Constraint**: The total number of students assigned to each classroom must not exceed its predefined capacity. This ensures that classroom sizes remain within acceptable limits.  
3. **Maximum Students Constraint**: The total number of students assigned to any classroom must be less than or equal to the maximum number of students allowed in any classroom. This directly supports the optimization objective of minimizing the maximum number of students in any classroom.  

These constraints are expressed in linear terms, ensuring that the optimization problem remains straightforward and avoids any nonlinear complexities.  

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 2 Database Schema
-- Objective: Added a new table for maximum students per classroom and updated business configuration logic to include a formula for calculating the maximum number of students in any classroom.

CREATE TABLE classroom_capacities (
  classroom_id INTEGER,
  capacity INTEGER
);

CREATE TABLE student_assignments (
  student_id INTEGER,
  classroom_id INTEGER,
  assignment BOOLEAN
);

CREATE TABLE max_students_per_classroom (
  classroom_id INTEGER,
  max_students INTEGER
);
```

### Data Dictionary  
- **classroom_capacities**:  
  - **Purpose**: Stores the maximum number of students allowed in each classroom.  
  - **Columns**:  
    - `classroom_id`: Unique identifier for each classroom.  
    - `capacity`: The maximum number of students allowed in the classroom.  

- **student_assignments**:  
  - **Purpose**: Tracks the assignment of students to classrooms.  
  - **Columns**:  
    - `student_id`: Unique identifier for each student.  
    - `classroom_id`: Unique identifier for each classroom.  
    - `assignment`: Indicates whether the student is assigned to the classroom (True or False).  

- **max_students_per_classroom**:  
  - **Purpose**: Represents the maximum number of students in any classroom.  
  - **Columns**:  
    - `classroom_id`: Unique identifier for each classroom.  
    - `max_students`: The maximum number of students in the classroom.  

### Current Stored Values  
```sql
-- Iteration 2 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical classroom sizes in a school district, ensuring that the number of students does not exceed classroom capacities and that the optimization problem remains meaningful.

-- Realistic data for classroom_capacities
INSERT INTO classroom_capacities (classroom_id, capacity) VALUES (1, 25);
INSERT INTO classroom_capacities (classroom_id, capacity) VALUES (2, 30);
INSERT INTO classroom_capacities (classroom_id, capacity) VALUES (3, 35);

-- Realistic data for student_assignments
INSERT INTO student_assignments (student_id, classroom_id, assignment) VALUES (101, 1, True);
INSERT INTO student_assignments (student_id, classroom_id, assignment) VALUES (102, 2, True);
INSERT INTO student_assignments (student_id, classroom_id, assignment) VALUES (103, 3, True);
INSERT INTO student_assignments (student_id, classroom_id, assignment) VALUES (104, 1, False);
INSERT INTO student_assignments (student_id, classroom_id, assignment) VALUES (105, 2, False);

-- Realistic data for max_students_per_classroom
INSERT INTO max_students_per_classroom (classroom_id, max_students) VALUES (1, 25);
INSERT INTO max_students_per_classroom (classroom_id, max_students) VALUES (2, 30);
INSERT INTO max_students_per_classroom (classroom_id, max_students) VALUES (3, 35);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_{s,c} \) be a binary decision variable where:
  - \( x_{s,c} = 1 \) if student \( s \) is assigned to classroom \( c \),
  - \( x_{s,c} = 0 \) otherwise.
- Let \( z \) be a continuous decision variable representing the maximum number of students in any classroom.

#### Objective Function
Minimize \( z \)

#### Constraints
1. **Student Assignment Constraint**: Each student must be assigned to exactly one classroom.
   \[
   \sum_{c \in C} x_{s,c} = 1 \quad \forall s \in S
   \]
2. **Classroom Capacity Constraint**: The total number of students assigned to each classroom must not exceed its predefined capacity.
   \[
   \sum_{s \in S} x_{s,c} \leq \text{classroom\_capacities.capacity}_c \quad \forall c \in C
   \]
3. **Maximum Students Constraint**: The total number of students assigned to any classroom must be less than or equal to the maximum number of students allowed in any classroom.
   \[
   \sum_{s \in S} x_{s,c} \leq z \quad \forall c \in C
   \]

#### Data Source Verification
- **Objective Function**: The variable \( z \) is derived from the optimization goal to minimize the maximum number of students in any classroom.
- **Student Assignment Constraint**: Ensures each student is assigned to exactly one classroom, as per the `student_assignments` table.
- **Classroom Capacity Constraint**: The capacity for each classroom \( c \) is taken from `classroom_capacities.capacity`.
- **Maximum Students Constraint**: The variable \( z \) is used to represent the maximum number of students in any classroom, aligning with the `max_students_per_classroom.max_students` values.

This formulation provides a complete, immediately solvable LINEAR mathematical model with all numerical coefficients derived from the provided data.

## 5. Gurobipy Implementation

```python
#!/usr/bin/env python3
"""
Gurobipy Implementation for Student Classroom Assignment Problem
"""

import gurobipy as gp
from gurobipy import GRB

def optimize_student_assignment():
    """Optimize student assignment to classrooms to minimize maximum class size."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("student_assignment")
    
    # Example data (replace with actual data loading)
    students = [101, 102, 103, 104, 105]
    classrooms = [1, 2, 3]
    classroom_capacities = {1: 25, 2: 30, 3: 35}
    max_students_per_classroom = {1: 25, 2: 30, 3: 35}
    
    # CRITICAL: Validate array lengths before loops
    assert len(classrooms) == len(classroom_capacities) == len(max_students_per_classroom), "Array length mismatch"
    
    # 2. VARIABLES
    # Binary decision variables for student assignments
    x = {(s, c): model.addVar(vtype=GRB.BINARY, name=f"x_{s}_{c}") 
         for s in students for c in classrooms}
    
    # Continuous variable for maximum number of students in any classroom
    z = model.addVar(vtype=GRB.CONTINUOUS, name="z", lb=0)
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(z, GRB.MINIMIZE)
    
    # 4. CONSTRAINTS
    
    # Student Assignment Constraint: Each student must be assigned to exactly one classroom
    for s in students:
        model.addConstr(gp.quicksum(x[s, c] for c in classrooms) == 1, name=f"student_assignment_{s}")
    
    # Classroom Capacity Constraint: Total students in each classroom must not exceed its capacity
    for c in classrooms:
        model.addConstr(gp.quicksum(x[s, c] for s in students) <= classroom_capacities[c], name=f"capacity_{c}")
    
    # Maximum Students Constraint: Total students in any classroom must be <= z
    for c in classrooms:
        model.addConstr(gp.quicksum(x[s, c] for s in students) <= z, name=f"max_students_{c}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for s in students:
            for c in classrooms:
                if x[s, c].x > 0.5:  # Check if assignment is True
                    print(f"Student {s} is assigned to Classroom {c}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization
if __name__ == "__main__":
    optimize_student_assignment()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 2.0
**Execution Time**: 0.19 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution efficiently with the lowest execution time.

## 6. DOCplex Implementation

```python
#!/usr/bin/env python3
"""
DOCplex implementation for student classroom assignment optimization
"""

from docplex.mp.model import Model

def optimize_student_assignments():
    """Optimize student assignments to classrooms to minimize maximum class size"""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="student_classroom_assignment")
    
    # Data from the problem context
    classrooms = [1, 2, 3]
    students = [101, 102, 103, 104, 105]
    
    classroom_capacities = {1: 25, 2: 30, 3: 35}
    max_students_per_classroom = {1: 25, 2: 30, 3: 35}
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(classrooms) == len(classroom_capacities) == len(max_students_per_classroom), "Array length mismatch"
    
    # 2. VARIABLES
    # Binary decision variables for student assignments
    x = {(s, c): mdl.binary_var(name=f"x_{s}_{c}") for s in students for c in classrooms}
    
    # Continuous variable for maximum number of students in any classroom
    z = mdl.continuous_var(name="z", lb=0)
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the maximum number of students in any classroom
    mdl.minimize(z)
    
    # 4. CONSTRAINTS
    
    # Student Assignment Constraint: Each student must be assigned to exactly one classroom
    for s in students:
        mdl.add_constraint(mdl.sum(x[s, c] for c in classrooms) == 1, ctname=f"student_assignment_{s}")
    
    # Classroom Capacity Constraint: Total students in each classroom must not exceed its capacity
    for c in classrooms:
        mdl.add_constraint(mdl.sum(x[s, c] for s in students) <= classroom_capacities[c], ctname=f"capacity_{c}")
    
    # Maximum Students Constraint: Total students in any classroom must be <= z
    for c in classrooms:
        mdl.add_constraint(mdl.sum(x[s, c] for s in students) <= z, ctname=f"max_students_{c}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for s in students:
            for c in classrooms:
                if solution.get_value(x[s, c]) > 0.5:
                    print(f"Student {s} is assigned to Classroom {c}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Execute the optimization
if __name__ == "__main__":
    optimize_student_assignments()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 2.0
**Execution Time**: 1.14 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution but took longer compared to Gurobipy.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def optimize_student_assignment():
    """Optimize student assignment to classrooms to minimize the maximum number of students in any classroom."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Example data (replace with actual data from database)
    students = [101, 102, 103, 104, 105]
    classrooms = [1, 2, 3]
    classroom_capacities = {1: 25, 2: 30, 3: 35}
    max_students_per_classroom = {1: 25, 2: 30, 3: 35}
    
    # CRITICAL: Validate array lengths before indexing
    assert len(classroom_capacities) == len(max_students_per_classroom) == len(classrooms), "Array length mismatch"
    
    # 3. SETS (Pyomo way to define indices)
    model.S = pyo.Set(initialize=students)  # Set of students
    model.C = pyo.Set(initialize=classrooms)  # Set of classrooms
    
    # 4. PARAMETERS (data containers)
    model.capacity = pyo.Param(model.C, initialize=classroom_capacities)
    model.max_students = pyo.Param(model.C, initialize=max_students_per_classroom)
    
    # 5. VARIABLES
    # Binary decision variable: x[s,c] = 1 if student s is assigned to classroom c
    model.x = pyo.Var(model.S, model.C, within=pyo.Binary)
    
    # Continuous decision variable: z represents the maximum number of students in any classroom
    model.z = pyo.Var(within=pyo.NonNegativeReals)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return model.z
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.minimize)
    
    # 7. CONSTRAINTS
    
    # Student Assignment Constraint: Each student must be assigned to exactly one classroom
    def student_assignment_rule(model, s):
        return sum(model.x[s, c] for c in model.C) == 1
    model.student_assignment = pyo.Constraint(model.S, rule=student_assignment_rule)
    
    # Classroom Capacity Constraint: The total number of students assigned to each classroom must not exceed its predefined capacity
    def classroom_capacity_rule(model, c):
        return sum(model.x[s, c] for s in model.S) <= model.capacity[c]
    model.classroom_capacity = pyo.Constraint(model.C, rule=classroom_capacity_rule)
    
    # Maximum Students Constraint: The total number of students assigned to any classroom must be less than or equal to z
    def max_students_rule(model, c):
        return sum(model.x[s, c] for s in model.S) <= model.z
    model.max_students_constraint = pyo.Constraint(model.C, rule=max_students_rule)
    
    # 8. SOLVING WITH GUROBI (your available solver)
    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("\nVariable values:")
        for s in model.S:
            for c in model.C:
                if pyo.value(model.x[s, c]) > 0.5:  # Only print assignments
                    print(f"Student {s} is assigned to Classroom {c}")
        
    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

# Execute the optimization
if __name__ == "__main__":
    optimize_student_assignment()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 2.0
**Execution Time**: 0.93 seconds
**Reliability**: high
**Analysis**: Pyomo found an optimal solution with an execution time between Gurobipy and DOCplex.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 2.00 | 0.19s | N/A | N/A |
| Docplex | OPTIMAL | 2.00 | 1.14s | N/A | N/A |
| Pyomo | OPTIMAL | 2.00 | 0.93s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 2.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is preferred due to its faster execution time while still providing a reliable optimal solution.

### Business Interpretation
**Overall Strategy**: The optimal solution ensures that the maximum number of students in any classroom is minimized to 2, balancing classroom utilization and student distribution.
**Objective Value Meaning**: The optimal objective value of 2.0 means that no classroom will have more than 2 students, ensuring balanced classroom sizes.
**Resource Allocation Summary**: Students should be evenly distributed across classrooms, with no classroom exceeding 2 students.
**Implementation Recommendations**: Implement the student assignments as per the optimal solution, ensuring compliance with classroom capacities and balanced distribution.