# Complete Optimization Problem and Solution: workshop_paper

## 1. Problem Context and Goals

### Context  
The business problem involves optimizing the selection of workshop submissions to maximize the overall quality of accepted papers while respecting the capacity constraints of each workshop. Each submission is evaluated and assigned a score that represents its quality. The decision to accept or reject a submission is binary, meaning a submission is either accepted (1) or rejected (0). The operational goal is to maximize the total quality score of all accepted submissions across all workshops. This is achieved by summing the scores of the accepted submissions, ensuring that the number of accepted submissions for each workshop does not exceed its predefined capacity. The business configuration includes a scalar parameter for the submission score, which is used as the coefficient in the objective function. The objective function is designed to maximize the total score of accepted submissions, aligning with the business goal of enhancing the overall quality of the workshop.

### Goals  
The primary goal of this optimization problem is to maximize the total quality score of the accepted workshop submissions. This is measured by summing the scores of all submissions that are accepted, ensuring that the selection process adheres to the capacity limits of each workshop. Success is determined by achieving the highest possible total score while respecting the workshop capacity constraints, thereby ensuring a high-quality and well-organized workshop.

## 2. Constraints    

The optimization problem must adhere to the following constraints:  
1. **Workshop Capacity Constraint**: The total number of accepted submissions for each workshop must not exceed the workshop's capacity. This ensures that the workshop does not become overcrowded and maintains a manageable number of presentations.  
2. **Binary Decision Constraint**: Each submission must be either accepted or rejected, represented by a binary decision variable. This ensures a clear and definitive selection process for each submission.  

These constraints are designed to ensure that the optimization problem remains linear and avoids any nonlinear relationships, such as variable products or divisions.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 2 Database Schema
-- Objective: Added submission_scores table to address missing Scores[i] mapping. Updated business configuration logic with scalar parameters for submission scores and formulas for optimization objective.

CREATE TABLE workshop_capacity (
  workshop_id INTEGER,
  capacity INTEGER
);

CREATE TABLE submission_workshop_mapping (
  submission_id INTEGER,
  workshop_id INTEGER,
  accepted BOOLEAN
);

CREATE TABLE submission_scores (
  submission_id INTEGER,
  score FLOAT
);
```

### Data Dictionary  
- **workshop_capacity**:  
  - **business_purpose**: Defines the maximum number of submissions that can be accepted for each workshop.  
  - **optimization_role**: Provides the bounds for the workshop capacity constraints.  
  - **columns**:  
    - **workshop_id**: Unique identifier for the workshop, used to index the capacity constraint.  
    - **capacity**: Maximum number of submissions that can be accepted for the workshop, serving as the bound for the constraint.  

- **submission_workshop_mapping**:  
  - **business_purpose**: Maps each submission to a specific workshop.  
  - **optimization_role**: Contains the business data used to determine which submissions are assigned to which workshops.  
  - **columns**:  
    - **submission_id**: Unique identifier for the submission, used to index the decision variable.  
    - **workshop_id**: Unique identifier for the workshop, used to index the capacity constraint.  
    - **accepted**: Binary indicator of whether the submission is accepted, serving as the decision variable in the optimization model.  

- **submission_scores**:  
  - **business_purpose**: Stores the quality scores assigned to each submission.  
  - **optimization_role**: Provides the coefficients for the objective function.  
  - **columns**:  
    - **submission_id**: Unique identifier for the submission, used to index the score.  
    - **score**: Quality score of the submission, used as the coefficient in the objective function.  

### Current Stored Values  
```sql
-- Iteration 2 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical workshop submission scenarios, ensuring a balance between quality scores and workshop capacities to create a meaningful optimization problem.

-- Realistic data for workshop_capacity
INSERT INTO workshop_capacity (workshop_id, capacity) VALUES (1, 12);
INSERT INTO workshop_capacity (workshop_id, capacity) VALUES (2, 15);
INSERT INTO workshop_capacity (workshop_id, capacity) VALUES (3, 10);

-- Realistic data for submission_workshop_mapping
INSERT INTO submission_workshop_mapping (submission_id, workshop_id, accepted) VALUES (1, 1, False);
INSERT INTO submission_workshop_mapping (submission_id, workshop_id, accepted) VALUES (2, 2, False);
INSERT INTO submission_workshop_mapping (submission_id, workshop_id, accepted) VALUES (3, 3, False);

-- Realistic data for submission_scores
INSERT INTO submission_scores (submission_id, score) VALUES (1, 8.7);
INSERT INTO submission_scores (submission_id, score) VALUES (2, 7.5);
INSERT INTO submission_scores (submission_id, score) VALUES (3, 9.2);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_i \) be a binary decision variable where:
  - \( x_i = 1 \) if submission \( i \) is accepted,
  - \( x_i = 0 \) if submission \( i \) is rejected.

#### Objective Function
Maximize the total quality score of accepted submissions:
\[
\text{Maximize} \quad \sum_{i} s_i \cdot x_i
\]
where \( s_i \) is the score of submission \( i \).

#### Constraints
1. **Workshop Capacity Constraint**: The total number of accepted submissions for each workshop \( j \) must not exceed its capacity \( C_j \):
\[
\sum_{i \in W_j} x_i \leq C_j \quad \forall j
\]
where \( W_j \) is the set of submissions assigned to workshop \( j \).

2. **Binary Decision Constraint**: Each submission must be either accepted or rejected:
\[
x_i \in \{0, 1\} \quad \forall i
\]

#### Data Source Verification
- **Objective Function Coefficient \( s_i \)**: `submission_scores.score`
- **Workshop Capacity \( C_j \)**: `workshop_capacity.capacity`
- **Submission-Workshop Mapping \( W_j \)**: `submission_workshop_mapping.workshop_id`

### Complete Numerical Model
Using the provided data:

#### Decision Variables
- \( x_1 \): Decision for submission 1
- \( x_2 \): Decision for submission 2
- \( x_3 \): Decision for submission 3

#### Objective Function
\[
\text{Maximize} \quad 8.7x_1 + 7.5x_2 + 9.2x_3
\]

#### Constraints
1. **Workshop Capacity Constraints**:
   - Workshop 1: \( x_1 \leq 12 \)
   - Workshop 2: \( x_2 \leq 15 \)
   - Workshop 3: \( x_3 \leq 10 \)

2. **Binary Decision Constraints**:
   - \( x_1 \in \{0, 1\} \)
   - \( x_2 \in \{0, 1\} \)
   - \( x_3 \in \{0, 1\} \)

This is a complete, immediately solvable Linear Programming (LP) model with all numerical coefficients derived from the provided data.

## 5. Gurobipy Implementation

```python
#!/usr/bin/env python3
"""
Gurobipy implementation for workshop paper selection optimization
"""

import gurobipy as gp
from gurobipy import GRB

def optimize_workshop_selection():
    """Optimize the selection of workshop submissions to maximize quality scores."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("workshop_paper_selection")
    
    # Data from the problem
    workshop_capacity = {1: 12, 2: 15, 3: 10}
    submission_scores = {1: 8.7, 2: 7.5, 3: 9.2}
    submission_workshop_mapping = {1: 1, 2: 2, 3: 3}
    
    # Validate array lengths
    assert len(submission_scores) == len(submission_workshop_mapping), "Array length mismatch"
    
    # 2. VARIABLES
    # Binary decision variables for each submission
    x = {i: model.addVar(vtype=GRB.BINARY, name=f"x_{i}") for i in submission_scores.keys()}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total quality score of accepted submissions
    model.setObjective(gp.quicksum(submission_scores[i] * x[i] for i in submission_scores.keys()), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    
    # Workshop Capacity Constraints
    for workshop_id, capacity in workshop_capacity.items():
        # Get submissions assigned to this workshop
        submissions_in_workshop = [i for i, w_id in submission_workshop_mapping.items() if w_id == workshop_id]
        model.addConstr(gp.quicksum(x[i] for i in submissions_in_workshop) <= capacity, name=f"capacity_{workshop_id}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in submission_scores.keys():
            if x[i].x > 0.5:
                print(f"Submission {i} is accepted")
            else:
                print(f"Submission {i} is rejected")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
if __name__ == "__main__":
    optimize_workshop_selection()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 25.4
**Execution Time**: 0.20 seconds
**Reliability**: high
**Analysis**: Gurobipy found the optimal solution quickly with the lowest execution time.

## 6. DOCplex Implementation

```python
#!/usr/bin/env python3
"""
DOCplex implementation for workshop paper selection optimization
"""

from docplex.mp.model import Model

def workshop_paper_optimization():
    """Optimize the selection of workshop submissions to maximize quality scores"""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="workshop_paper_selection")
    
    # Data from the database
    workshop_capacity = {1: 12, 2: 15, 3: 10}
    submission_scores = {1: 8.7, 2: 7.5, 3: 9.2}
    submission_workshop_mapping = {1: 1, 2: 2, 3: 3}
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(submission_scores) == len(submission_workshop_mapping), "Array length mismatch"
    safe_range = range(min(len(submission_scores), len(submission_workshop_mapping)))  # Safe indexing
    
    # 2. VARIABLES
    # Binary decision variables for each submission
    x = {i: mdl.binary_var(name=f"x_{i}") for i in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total quality score of accepted submissions
    objective = mdl.sum(submission_scores[i+1] * x[i] for i in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Workshop Capacity Constraints
    # Group submissions by workshop
    workshop_submissions = {j: [i for i in safe_range if submission_workshop_mapping[i+1] == j] for j in workshop_capacity}
    
    for j, submissions in workshop_submissions.items():
        mdl.add_constraint(mdl.sum(x[i] for i in submissions) <= workshop_capacity[j], ctname=f"workshop_capacity_{j}")
    
    # Binary Decision Constraints are inherently handled by the binary_var type
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_range:
            value = solution.get_value(x[i])
            print(f"x[{i+1}] = {value}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
if __name__ == "__main__":
    workshop_paper_optimization()
```

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

## 7. Pyomo Implementation

```python
#!/usr/bin/env python3
"""
Pyomo 6.9.2 Implementation for Workshop Paper Selection Optimization
"""

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

def workshop_paper_optimization():
    """Optimize the selection of workshop submissions to maximize quality scores."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Workshop capacities
    workshop_capacity_data = {
        1: 12,
        2: 15,
        3: 10
    }
    
    # Submission scores
    submission_scores_data = {
        1: 8.7,
        2: 7.5,
        3: 9.2
    }
    
    # Submission to workshop mapping
    submission_workshop_mapping_data = {
        1: 1,
        2: 2,
        3: 3
    }
    
    # Validate data consistency
    submission_ids = set(submission_scores_data.keys())
    workshop_ids = set(workshop_capacity_data.keys())
    mapped_submissions = set(submission_workshop_mapping_data.keys())
    mapped_workshops = set(submission_workshop_mapping_data.values())
    
    assert submission_ids == mapped_submissions, "Submission IDs mismatch in mapping"
    assert mapped_workshops.issubset(workshop_ids), "Workshop IDs mismatch in mapping"
    
    # 3. SETS
    model.SUBMISSIONS = pyo.Set(initialize=submission_ids)
    model.WORKSHOPS = pyo.Set(initialize=workshop_ids)
    
    # 4. PARAMETERS
    model.scores = pyo.Param(model.SUBMISSIONS, initialize=submission_scores_data)
    model.capacities = pyo.Param(model.WORKSHOPS, initialize=workshop_capacity_data)
    model.mapping = pyo.Param(model.SUBMISSIONS, initialize=submission_workshop_mapping_data)
    
    # 5. VARIABLES
    model.accept = pyo.Var(model.SUBMISSIONS, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.scores[i] * model.accept[i] for i in model.SUBMISSIONS)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    # Workshop capacity constraints
    def capacity_rule(model, j):
        return sum(model.accept[i] for i in model.SUBMISSIONS if model.mapping[i] == j) <= model.capacities[j]
    model.capacity_constraint = pyo.Constraint(model.WORKSHOPS, rule=capacity_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(f"Optimal value: {pyo.value(model.objective)}")
        
        # Extract variable values
        print("\nAccepted submissions:")
        for i in model.SUBMISSIONS:
            if pyo.value(model.accept[i]) > 0.5:
                print(f"Submission {i} accepted (Score: {model.scores[i]})")
        
    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
if __name__ == "__main__":
    workshop_paper_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 25.4
**Execution Time**: 0.93 seconds
**Reliability**: high
**Analysis**: Pyomo found the 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 | 25.40 | 0.20s | N/A | N/A |
| Docplex | OPTIMAL | 25.40 | 1.16s | N/A | N/A |
| Pyomo | OPTIMAL | 25.40 | 0.93s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 25.4
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy provided the optimal solution with the fastest execution time, making it the most efficient choice.

### Optimal Decision Variables
- **x_1** = 1.000
  - *Business Meaning*: Submission 1 is accepted, contributing 8.7 to the total quality score.
- **x_2** = 1.000
  - *Business Meaning*: Submission 2 is accepted, contributing 7.5 to the total quality score.
- **x_3** = 1.000
  - *Business Meaning*: Submission 3 is accepted, contributing 9.2 to the total quality score.

### Business Interpretation
**Overall Strategy**: All three submissions should be accepted to maximize the total quality score, as they all fit within the workshop capacities.
**Objective Value Meaning**: The total quality score of 25.4 is the highest achievable given the workshop capacities.
**Resource Allocation Summary**: All workshops have sufficient capacity to accept their assigned submissions.
**Implementation Recommendations**: Proceed with accepting all three submissions and ensure that the workshops are prepared to accommodate them.