# Complete Optimization Problem and Solution: restaurants

## 1. Problem Context and Goals

### Context  
A restaurant chain is aiming to strategically allocate its marketing budget across various cities to enhance the overall ratings of its restaurants. The decision involves determining the amount of budget to allocate to each city, represented by the decision variable `BudgetAllocations.allocation[city_id]`, which is continuous. The primary objective is to maximize the expected improvement in restaurant ratings, which is directly influenced by the budget allocated to each city. Each city has a specific potential for rating improvement per unit of budget spent, captured by `CityRatingImprovementCoefficients.coefficient[city_id]`. The total marketing budget available for allocation is a fixed amount, serving as a constraint in the optimization model. This budget constraint ensures that the sum of all allocations does not exceed the total budget. The business configuration includes this total budget as a scalar parameter, ensuring that the allocation decisions are made within the available financial resources. The focus is on making precise operational decisions that align with linear optimization principles, avoiding any nonlinear relationships such as variable products or divisions.

### Goals  
The optimization goal is to maximize the overall improvement in restaurant ratings across all cities. This is achieved by optimizing the sum of the expected rating improvements, which is calculated by multiplying the potential rating improvement per unit of budget (`CityRatingImprovementCoefficients.coefficient[city_id]`) by the budget allocated to each city (`BudgetAllocations.allocation[city_id]`). Success is measured by the extent to which the total expected rating improvement is maximized, aligning with the linear optimization objective. The goal is clearly defined in natural language, focusing on the linear relationship between budget allocation and rating improvement.

## 2. Constraints    

The optimization problem is subject to specific constraints that ensure the solution is feasible within the business context. The primary constraint is that the total budget allocated across all cities must not exceed the total marketing budget available. This is expressed as the sum of all budget allocations being less than or equal to the total budget. Additionally, each city's budget allocation must be non-negative, ensuring that no city receives a negative budget. These constraints are described in business terms, naturally leading to linear mathematical forms without involving any nonlinear relationships.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include creating tables for decision variables and objective coefficients, and updating business configuration logic for constraint bounds.

CREATE TABLE CityRatingImprovementCoefficients (
  city_id INTEGER,
  coefficient FLOAT
);

CREATE TABLE BudgetAllocations (
  city_id INTEGER,
  allocation FLOAT
);
```

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

- **CityRatingImprovementCoefficients**: This table stores the expected rating improvement per unit of budget spent in each city. It plays a crucial role in the optimization as it provides the coefficients for the objective function.
  - **city_id**: A unique identifier for each city, used to index city-specific coefficients.
  - **coefficient**: Represents the expected rating improvement per unit of budget, serving as a coefficient in the objective function.

- **BudgetAllocations**: This table records the amount of budget allocated to each city, representing the decision variables in the optimization model.
  - **city_id**: A unique identifier for each city, used to index budget allocations.
  - **allocation**: The amount of budget allocated to the city, serving as a decision variable in the optimization model.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical marketing budget allocations and expected rating improvements in the restaurant industry, ensuring a balance between potential improvements and budget constraints.

-- Realistic data for CityRatingImprovementCoefficients
INSERT INTO CityRatingImprovementCoefficients (city_id, coefficient) VALUES (1, 0.15);
INSERT INTO CityRatingImprovementCoefficients (city_id, coefficient) VALUES (2, 0.25);
INSERT INTO CityRatingImprovementCoefficients (city_id, coefficient) VALUES (3, 0.1);

-- Realistic data for BudgetAllocations
INSERT INTO BudgetAllocations (city_id, allocation) VALUES (1, 30000);
INSERT INTO BudgetAllocations (city_id, allocation) VALUES (2, 50000);
INSERT INTO BudgetAllocations (city_id, allocation) VALUES (3, 20000);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_i \) represent the budget allocation for city \( i \).
- \( x_i \geq 0 \) for all cities \( i \).

#### Objective Function
Maximize the total expected improvement in restaurant ratings across all cities:
\[
\text{Maximize } Z = 0.15x_1 + 0.25x_2 + 0.10x_3
\]

#### Constraints
1. Total budget constraint:
   \[
   x_1 + x_2 + x_3 \leq 100000
   \]
2. Non-negativity constraints:
   \[
   x_1 \geq 0, \quad x_2 \geq 0, \quad x_3 \geq 0
   \]

Data Source Verification:
- The coefficients \( 0.15 \), \( 0.25 \), and \( 0.10 \) in the objective function are sourced from `CityRatingImprovementCoefficients.coefficient` for cities 1, 2, and 3, respectively.
- The total budget constraint \( 100000 \) is derived from the business configuration parameter representing the total marketing budget available.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_budget_allocation():
    """Optimize marketing budget allocation across cities to maximize rating improvement."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("restaurant_budget_allocation")
    
    # Data: Coefficients for rating improvement per unit of budget
    city_ids = [1, 2, 3]
    coefficients = [0.15, 0.25, 0.10]
    total_budget = 100000
    
    # CRITICAL: Validate array lengths before loops
    assert len(city_ids) == len(coefficients), "Array length mismatch"
    
    # 2. VARIABLES
    # Variable dictionaries for budget allocations
    allocation = {city_id: model.addVar(vtype=GRB.CONTINUOUS, name=f"allocation_{city_id}", lb=0) 
                  for city_id in city_ids}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total expected improvement in restaurant ratings
    model.setObjective(gp.quicksum(coefficients[i] * allocation[city_ids[i]] for i in range(len(city_ids))), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Total budget constraint
    model.addConstr(gp.quicksum(allocation[city_id] for city_id in city_ids) <= total_budget, name="budget_constraint")
    
    # Non-negativity constraints are implicitly handled by the lower bound in variable definition
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for city_id in city_ids:
            if allocation[city_id].x > 1e-6:
                print(f"Allocation for city {city_id}: {allocation[city_id].x:.2f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_budget_allocation()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 25000.0
**Execution Time**: 0.62 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with a clear allocation of the entire budget to city 2, which has the highest coefficient in the objective function.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_marketing_budget():
    """Optimize marketing budget allocation across cities to maximize rating improvement."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="restaurant_marketing")
    
    # Coefficients for rating improvement per unit of budget
    coefficients = [0.15, 0.25, 0.10]
    
    # Total marketing budget available
    total_budget = 100000
    
    # Number of cities
    n_cities = len(coefficients)
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(coefficients) == n_cities, "Array length mismatch"
    safe_range = range(n_cities)  # Safe indexing
    
    # 2. VARIABLES
    # Variable dictionaries for budget allocations
    allocation = {i: mdl.continuous_var(name=f"allocation_{i}", lb=0) for i in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total expected improvement in restaurant ratings
    objective = mdl.sum(coefficients[i] * allocation[i] for i in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Total budget constraint
    total_allocation = mdl.sum(allocation[i] for i in safe_range)
    mdl.add_constraint(total_allocation <= total_budget, ctname="total_budget_constraint")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_range:
            value = solution.get_value(allocation[i])
            if value > 1e-6:
                print(f"allocation[{i}] = {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
optimize_marketing_budget()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 25000.0
**Execution Time**: 3.58 seconds
**Reliability**: high
**Analysis**: DOCplex also found the same optimal solution, confirming the allocation to city 2. Execution time was longer but still reasonable.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def restaurant_marketing_optimization():
    """Optimize marketing budget allocation across cities to maximize rating improvement."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    city_ids = [1, 2, 3]
    coefficients = {1: 0.15, 2: 0.25, 3: 0.10}
    total_budget = 100000
    
    # CRITICAL: Validate array lengths before indexing
    assert len(coefficients) == len(city_ids), "Array length mismatch"
    
    # 3. SETS
    model.C = pyo.Set(initialize=city_ids)
    
    # 4. PARAMETERS
    model.coefficient = pyo.Param(model.C, initialize=coefficients)
    
    # 5. VARIABLES
    model.allocation = pyo.Var(model.C, within=pyo.NonNegativeReals)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.coefficient[c] * model.allocation[c] for c in model.C)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    def budget_constraint_rule(model):
        return sum(model.allocation[c] for c in model.C) <= total_budget
    model.budget_constraint = pyo.Constraint(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):.3f}")
        
        # Extract variable values
        print("\nBudget Allocations:")
        for c in model.C:
            allocation_val = pyo.value(model.allocation[c])
            if allocation_val > 1e-6:  # Only print non-zero values
                print(f"City {c}: {allocation_val:.2f}")
        
    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
restaurant_marketing_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 25000.0
**Execution Time**: 2.06 seconds
**Reliability**: high
**Analysis**: Pyomo confirmed the same optimal solution as the other solvers, with a moderate execution time.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 25000.00 | 0.62s | N/A | N/A |
| Docplex | OPTIMAL | 25000.00 | 3.58s | N/A | N/A |
| Pyomo | OPTIMAL | 25000.00 | 2.06s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 25000.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: multiple
**Reasoning**: All solvers provided consistent results, confirming the reliability of the solution. The allocation to city 2 is optimal due to its highest coefficient in the objective function.

### Optimal Decision Variables
- **x_1** = 0.000
  - *Business Meaning*: Budget allocation for city 2, which should receive the entire budget to maximize impact.
- **x_2** = 100000.000
  - *Business Meaning*: Budget allocation for city 3, which is not recommended in this solution.
- **x_3** = 0.000
  - *Business Meaning*: Resource allocation for x_3

### Business Interpretation
**Overall Strategy**: Allocate the entire budget to city 2 to maximize the expected improvement in restaurant ratings.
**Objective Value Meaning**: The optimal objective value of 25000 represents the maximum expected improvement in restaurant ratings achievable with the given budget.
**Resource Allocation Summary**: Allocate the entire budget of 100000 to city 2 to achieve the highest possible improvement in restaurant ratings.
**Implementation Recommendations**: Focus marketing efforts and resources on city 2 to leverage the highest potential improvement in restaurant ratings. Monitor the impact and adjust strategies as needed.