# Complete Optimization Problem and Solution: restaurant_1

## 1. Problem Context and Goals

### Context  
The objective is to optimize how students allocate their visits to various restaurants to achieve the highest possible satisfaction score. Each student has a specific budget that limits their total spending on restaurant visits. The decision involves determining the number of visits each student should make to each restaurant. The satisfaction score is derived from the restaurant's rating, which is multiplied by the number of visits. The budget constraint for each student is a critical factor, ensuring that the total cost of visits does not exceed their allocated budget. The cost per visit for each student-restaurant pair is used to calculate the total spending, ensuring that the optimization remains within linear constraints. This setup allows for precise decision-making, focusing on maximizing satisfaction while adhering to budgetary limits.

### Goals  
The primary goal is to maximize the total satisfaction score for all students. This is achieved by optimizing the number of visits each student makes to each restaurant, with the satisfaction score being the product of the restaurant's rating and the number of visits. The success of this optimization is measured by the total satisfaction score, which is directly influenced by the restaurant ratings and the number of visits, ensuring a clear linear relationship in the optimization process.

## 2. Constraints    

The optimization is subject to several constraints. Firstly, the total cost incurred by a student from visiting restaurants must not exceed their budget. This ensures that each student's spending remains within their financial limits. Additionally, the number of visits a student makes to any restaurant must be a non-negative integer, reflecting the realistic scenario where visits cannot be negative or fractional. These constraints are designed to maintain a linear relationship, ensuring the optimization problem remains solvable within the defined parameters.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include adding tables for budget constraints and cost per visit, modifying existing tables to include necessary columns, and updating configuration logic for scalar parameters and formulas.

CREATE TABLE Restaurant (
  ResID INTEGER,
  Rating FLOAT
);

CREATE TABLE Student_Budget (
  StuID INTEGER,
  Budget INTEGER
);

CREATE TABLE Cost_Per_Visit (
  StuID INTEGER,
  ResID INTEGER,
  Cost FLOAT
);

CREATE TABLE Visits_Restaurant (
  StuID INTEGER,
  ResID INTEGER,
  Visits INTEGER
);
```

### Data Dictionary  
- **Restaurant Table**: This table holds information about each restaurant, including a unique identifier and a rating that reflects the satisfaction score a student receives from visiting the restaurant. The rating serves as a coefficient in the optimization objective, influencing the total satisfaction score.

- **Student_Budget Table**: This table contains the budget constraints for each student, identified by a unique student ID. The budget represents the maximum amount a student can spend on restaurant visits, serving as a constraint bound in the optimization model.

- **Cost_Per_Visit Table**: This table records the cost associated with each visit for a student to a specific restaurant. It includes identifiers for both the student and the restaurant, and the cost per visit is used in calculating the total spending, ensuring it aligns with the budget constraints.

- **Visits_Restaurant Table**: This table tracks the number of visits each student makes to each restaurant. It includes identifiers for both the student and the restaurant, and the number of visits is the decision variable in the optimization model, directly affecting the total satisfaction score.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical student budgets and restaurant costs, ensuring a variety of scenarios for optimization. Ratings were chosen to reflect a range of restaurant quality.

-- Realistic data for Restaurant
INSERT INTO Restaurant (ResID, Rating) VALUES (1, 4.5);
INSERT INTO Restaurant (ResID, Rating) VALUES (2, 3.8);
INSERT INTO Restaurant (ResID, Rating) VALUES (3, 5.0);

-- Realistic data for Student_Budget
INSERT INTO Student_Budget (StuID, Budget) VALUES (101, 120);
INSERT INTO Student_Budget (StuID, Budget) VALUES (102, 180);
INSERT INTO Student_Budget (StuID, Budget) VALUES (103, 150);

-- Realistic data for Cost_Per_Visit
INSERT INTO Cost_Per_Visit (StuID, ResID, Cost) VALUES (101, 1, 12.0);
INSERT INTO Cost_Per_Visit (StuID, ResID, Cost) VALUES (102, 2, 15.0);
INSERT INTO Cost_Per_Visit (StuID, ResID, Cost) VALUES (103, 3, 20.0);

-- Realistic data for Visits_Restaurant
INSERT INTO Visits_Restaurant (StuID, ResID, Visits) VALUES (101, 1, 3);
INSERT INTO Visits_Restaurant (StuID, ResID, Visits) VALUES (102, 2, 4);
INSERT INTO Visits_Restaurant (StuID, ResID, Visits) VALUES (103, 3, 2);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- \( x_{ij} \): Number of visits student \( i \) makes to restaurant \( j \).
  - \( x_{ij} \geq 0 \) and integer for all students \( i \) and restaurants \( j \).

#### Objective Function
Maximize the total satisfaction score across all students and restaurants:
\[ 
\text{Maximize } \sum_{i} \sum_{j} \text{Rating}_{j} \times x_{ij} 
\]
Where:
- \(\text{Rating}_{j}\) is the rating of restaurant \( j \) from the `Restaurant` table.

#### Constraints
1. Budget Constraints for each student:
   \[
   \sum_{j} \text{Cost}_{ij} \times x_{ij} \leq \text{Budget}_{i} \quad \forall i
   \]
   Where:
   - \(\text{Cost}_{ij}\) is the cost per visit for student \( i \) to restaurant \( j \) from the `Cost_Per_Visit` table.
   - \(\text{Budget}_{i}\) is the budget for student \( i \) from the `Student_Budget` table.

2. Non-negativity and integer constraints:
   \[
   x_{ij} \geq 0 \quad \text{and integer} \quad \forall i, j
   \]

Data Source Verification:
- \(\text{Rating}_{j}\) from `Restaurant.Rating`
- \(\text{Cost}_{ij}\) from `Cost_Per_Visit.Cost`
- \(\text{Budget}_{i}\) from `Student_Budget.Budget`

This formulation is a Mixed-Integer Linear Programming (MILP) model due to the integer constraints on the decision variables \( x_{ij} \). The objective function and constraints are linear, adhering to the requirements for linear optimization.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def restaurant_optimization():
    """Optimize student visits to restaurants for maximum satisfaction."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("restaurant_optimization")
    
    # Data from the problem statement
    restaurants = [1, 2, 3]
    students = [101, 102, 103]
    
    ratings = {1: 4.5, 2: 3.8, 3: 5.0}
    budgets = {101: 120, 102: 180, 103: 150}
    costs = {(101, 1): 12.0, (102, 2): 15.0, (103, 3): 20.0}
    
    # Validate data lengths
    assert len(ratings) == len(restaurants), "Ratings length mismatch"
    assert len(budgets) == len(students), "Budgets length mismatch"
    assert len(costs) == len(students) * len(restaurants), "Costs length mismatch"
    
    # 2. VARIABLES
    # Decision variables: number of visits each student makes to each restaurant
    visits = model.addVars(students, restaurants, vtype=GRB.INTEGER, name="visits", lb=0)
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total satisfaction score
    model.setObjective(gp.quicksum(ratings[j] * visits[i, j] for i in students for j in restaurants), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    # Budget constraints for each student
    for i in students:
        model.addConstr(gp.quicksum(costs[i, j] * visits[i, j] for j in restaurants if (i, j) in costs) <= budgets[i], name=f"budget_{i}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in students:
            for j in restaurants:
                if visits[i, j].x > 1e-6:
                    print(f"Student {i} visits Restaurant {j}: {visits[i, j].x:.0f} times")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
restaurant_optimization()
```

### Execution Results
**Status**: ERROR
**Error**: Traceback (most recent call last):
  File "/tmp/tmp_v7eeqr1.py", line 55, in <module>
    restaurant_optimization()
  File "/tmp/tmp_v7eeqr1.py", line 23, in restaurant_optimization
    assert len(costs) == len(students) * len(restaurants), "Costs length mismatch"
AssertionError: Costs length mismatch

**Analysis**: Gurobipy encountered an error due to a mismatch in the length of the costs array, indicating a potential issue with data preparation or input consistency.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation - Retry Attempt 4

from docplex.mp.model import Model

def restaurant_optimization():
    """Optimize student visits to restaurants for maximum satisfaction."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="restaurant_optimization")
    
    # Data from the problem statement
    restaurants = [1, 2, 3]
    students = [101, 102, 103]
    
    ratings = {1: 4.5, 2: 3.8, 3: 5.0}
    budgets = {101: 120, 102: 180, 103: 150}
    costs = {(101, 1): 12.0, (102, 2): 15.0, (103, 3): 20.0}
    
    # Validate data consistency
    assert len(ratings) == len(restaurants), "Ratings length mismatch"
    assert len(budgets) == len(students), "Budgets length mismatch"
    assert len(costs) == len(students) * len(restaurants), "Costs length mismatch"
    
    # Safe range for indexing
    safe_students = range(len(students))
    safe_restaurants = range(len(restaurants))
    
    # 2. VARIABLES
    # Decision variables: number of visits each student makes to each restaurant
    visits = {(i, j): mdl.integer_var(name=f"x_{i}_{j}", lb=0) for i in students for j in restaurants}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total satisfaction score
    objective = mdl.sum(ratings[j] * visits[i, j] for i in students for j in restaurants)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    # Budget constraints for each student
    for i in students:
        mdl.add_constraint(mdl.sum(costs[i, j] * visits[i, j] for j in restaurants) <= budgets[i], ctname=f"budget_{i}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in students:
            for j in restaurants:
                value = solution.get_value(visits[i, j])
                if value > 0:
                    print(f"Student {i} visits Restaurant {j}: {value} times")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")

# Run the optimization
restaurant_optimization()
```

### Execution Results
**Status**: ERROR
**Error**: Traceback (most recent call last):
  File "/tmp/tmpo10g2c37.py", line 57, in <module>
    restaurant_optimization()
  File "/tmp/tmpo10g2c37.py", line 22, in restaurant_optimization
    assert len(costs) == len(students) * len(restaurants), "Costs length mismatch"
AssertionError: Costs length mismatch

**Analysis**: DOCplex also faced a costs length mismatch error, similar to Gurobipy, suggesting a systemic issue with the input data.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation - Retry Attempt 4

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

def restaurant_optimization():
    """Optimize student visits to restaurants for maximum satisfaction."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Define the data based on the provided SQL data
    students = [101, 102, 103]
    restaurants = [1, 2, 3]
    
    ratings = {1: 4.5, 2: 3.8, 3: 5.0}
    budgets = {101: 120, 102: 180, 103: 150}
    costs = {(101, 1): 12.0, (102, 2): 15.0, (103, 3): 20.0}
    
    # CRITICAL: Validate array lengths before indexing
    assert len(costs) == len(students), "Costs length mismatch"
    
    # 3. SETS
    model.I = pyo.Set(initialize=students)
    model.J = pyo.Set(initialize=restaurants)
    
    # 4. PARAMETERS
    model.rating = pyo.Param(model.J, initialize=ratings)
    model.budget = pyo.Param(model.I, initialize=budgets)
    model.cost = pyo.Param(model.I, model.J, initialize=costs, default=0)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.I, model.J, within=pyo.NonNegativeIntegers)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.rating[j] * model.x[i, j] for i in model.I for j in model.J)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    def budget_constraint_rule(model, i):
        return sum(model.cost[i, j] * model.x[i, j] for j in model.J) <= model.budget[i]
    model.budget_constraint = pyo.Constraint(model.I, rule=budget_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("\nVariable values:")
        for i in model.I:
            for j in model.J:
                x_val = pyo.value(model.x[i, j])
                if x_val > 0:  # Only print non-zero values
                    print(f"x[{i},{j}] = {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

# Execute the optimization
restaurant_optimization()
```

### Execution Results
**Status**: UNBOUNDED
**Error**: Problem is unbounded
**Analysis**: Pyomo reported the problem as unbounded, which may indicate missing constraints or incorrect formulation, especially given the errors in other solvers.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | ERROR | N/A | 0.57s | N/A | 4 |
| Docplex | ERROR | N/A | 7.53s | N/A | 4 |
| Pyomo | UNBOUNDED | N/A | 3.12s | N/A | 4 |

### Solver Consistency Analysis
**Result**: Solvers produced inconsistent results
**Inconsistent Solvers**: gurobipy, docplex, pyomo
**Potential Issues**:
- Mismatch in costs data length
- Potential missing or incorrect constraints leading to unbounded solution
- Data preparation errors affecting all solvers
**Solver Retry Summary**: gurobipy: 4 attempts, docplex: 4 attempts, pyomo: 4 attempts

### Final Recommendation
**Confidence Level**: LOW
**Preferred Solver(s)**: multiple
**Reasoning**: All solvers encountered significant issues, indicating a need to revisit the data and formulation before a reliable solver preference can be established.

### Business Interpretation
**Overall Strategy**: The current results do not provide a valid solution due to data and formulation issues.
**Objective Value Meaning**: The optimal objective value would represent the maximum total satisfaction score achievable within the given budget constraints.
**Resource Allocation Summary**: Currently unavailable due to errors; requires correction of data and formulation.
**Implementation Recommendations**: Review and correct the input data, particularly the costs array, and ensure all constraints are properly defined and implemented.