# Complete Optimization Problem and Solution: journal_committee

## 1. Problem Context and Goals

### Context  
A publishing company is focused on maximizing the total sales of its journals by strategically assigning editors to journals. The company ensures that each journal is assigned at least one editor and that no editor is overburdened with too many journals. The decision to assign an editor to a journal is represented by a binary variable, where a value of true indicates an assignment and false indicates no assignment. The company uses the sales figures of each journal as a key metric to drive the optimization process. Additionally, the company has set a maximum workload limit for each editor, ensuring that no editor is assigned more journals than they can handle. The assignment process also considers the expertise of editors, ensuring that editors are matched to journals based on their thematic alignment. This expertise matching is calculated by comparing the editor's expertise theme with the journal's theme, resulting in a binary match indicator. The optimization problem is designed to be linear, avoiding any nonlinear relationships such as variable products or divisions.

### Goals  
The primary goal of this optimization problem is to maximize the total sales of journals by making optimal assignments of editors to journals. Success is measured by the sum of sales from all journals, where each journal's sales figure is multiplied by the assignment decision. The company aims to achieve this goal while respecting the constraints on editor workloads and ensuring that each journal is assigned at least one editor. The optimization process is designed to be linear, focusing on straightforward, additive relationships that align with the company's operational parameters.

## 2. Constraints    

The optimization problem is subject to two key constraints. First, the total number of journals assigned to each editor must not exceed the editor's maximum workload limit. This ensures that no editor is overburdened and can effectively manage their assigned journals. Second, each journal must be assigned at least one editor. This ensures that every journal has the necessary editorial oversight and support. Both constraints are designed to be linear, avoiding any complex relationships or nonlinear interactions.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include creating new tables for editor expertise and workload limits, modifying the journal_committee table to better represent assignments, and adding configuration logic for scalar parameters and formulas.

CREATE TABLE journal (
  Sales FLOAT
);

CREATE TABLE editor_expertise (
  theme STRING
);

CREATE TABLE editor_workload (
  Max_Workload INTEGER
);

CREATE TABLE journal_committee (
  Assign_ej BOOLEAN
);
```

### Data Dictionary  
- **journal**: Stores details about each journal, including its sales figures. The sales figures are used as coefficients in the objective function to maximize total sales.
  - **Sales**: Represents the sales of the journal, used as a coefficient in the objective function.

- **editor_expertise**: Stores information about the expertise of each editor, specifically the themes they are knowledgeable in. This information is used to match editors to journals based on thematic alignment.
  - **theme**: Represents the theme of expertise for an editor, used in the expertise matching formula.

- **editor_workload**: Stores the maximum number of journals each editor can handle. This information is used to set the constraint bounds for editor workloads.
  - **Max_Workload**: Represents the maximum number of journals an editor can handle, used as a constraint bound.

- **journal_committee**: Stores the assignments of editors to journals. The assignment decisions are represented as binary variables in the optimization model.
  - **Assign_ej**: Represents the assignment of an editor to a journal, used 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 realistic business scenarios, ensuring that editors' workloads are balanced, journals have varying sales figures, and expertise matches are meaningful.

-- Realistic data for journal
INSERT INTO journal (Sales) VALUES (1200.0);
INSERT INTO journal (Sales) VALUES (1800.0);
INSERT INTO journal (Sales) VALUES (900.0);

-- Realistic data for editor_expertise
INSERT INTO editor_expertise (theme) VALUES ('Science');
INSERT INTO editor_expertise (theme) VALUES ('Arts');
INSERT INTO editor_expertise (theme) VALUES ('Technology');

-- Realistic data for editor_workload
INSERT INTO editor_workload (Max_Workload) VALUES (4);
INSERT INTO editor_workload (Max_Workload) VALUES (3);
INSERT INTO editor_workload (Max_Workload) VALUES (5);

-- Realistic data for journal_committee
INSERT INTO journal_committee (Assign_ej) VALUES (True);
INSERT INTO journal_committee (Assign_ej) VALUES (False);
INSERT INTO journal_committee (Assign_ej) VALUES (True);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_{ej} \) be a binary decision variable where:
  - \( x_{ej} = 1 \) if editor \( e \) is assigned to journal \( j \),
  - \( x_{ej} = 0 \) otherwise.

#### Objective Function
Maximize the total sales of journals:
\[
\text{Maximize} \quad \sum_{e} \sum_{j} \text{Sales}_j \cdot x_{ej}
\]
where \( \text{Sales}_j \) is the sales figure of journal \( j \).

#### Constraints
1. **Editor Workload Constraint**: Each editor \( e \) cannot be assigned more journals than their maximum workload limit:
\[
\sum_{j} x_{ej} \leq \text{Max\_Workload}_e \quad \forall e
\]
where \( \text{Max\_Workload}_e \) is the maximum number of journals editor \( e \) can handle.

2. **Journal Assignment Constraint**: Each journal \( j \) must be assigned at least one editor:
\[
\sum_{e} x_{ej} \geq 1 \quad \forall j
\]

#### Data Source Verification
- **Sales_j**: Coefficient in the objective function, sourced from `journal.Sales`.
- **Max_Workload_e**: Coefficient in the editor workload constraint, sourced from `editor_workload.Max_Workload`.
- **x_ej**: Decision variable representing the assignment of editor \( e \) to journal \( j \), sourced from `journal_committee.Assign_ej`.

### Complete Numerical Model
Given the provided data:

- **Journals**: \( j = \{1, 2, 3\} \) with sales \( \text{Sales}_1 = 1200.0 \), \( \text{Sales}_2 = 1800.0 \), \( \text{Sales}_3 = 900.0 \).
- **Editors**: \( e = \{1, 2, 3\} \) with maximum workloads \( \text{Max\_Workload}_1 = 4 \), \( \text{Max\_Workload}_2 = 3 \), \( \text{Max\_Workload}_3 = 5 \).

#### Objective Function
\[
\text{Maximize} \quad 1200.0 \cdot x_{11} + 1200.0 \cdot x_{21} + 1200.0 \cdot x_{31} + 1800.0 \cdot x_{12} + 1800.0 \cdot x_{22} + 1800.0 \cdot x_{32} + 900.0 \cdot x_{13} + 900.0 \cdot x_{23} + 900.0 \cdot x_{33}
\]

#### Constraints
1. **Editor Workload Constraints**:
\[
x_{11} + x_{12} + x_{13} \leq 4 \quad \text{(Editor 1)}
\]
\[
x_{21} + x_{22} + x_{23} \leq 3 \quad \text{(Editor 2)}
\]
\[
x_{31} + x_{32} + x_{33} \leq 5 \quad \text{(Editor 3)}
\]

2. **Journal Assignment Constraints**:
\[
x_{11} + x_{21} + x_{31} \geq 1 \quad \text{(Journal 1)}
\]
\[
x_{12} + x_{22} + x_{32} \geq 1 \quad \text{(Journal 2)}
\]
\[
x_{13} + x_{23} + x_{33} \geq 1 \quad \text{(Journal 3)}
\]

This is a complete, immediately solvable Mixed-Integer Linear Programming (MILP) model.

## 5. Gurobipy Implementation

```python
#!/usr/bin/env python3
"""
Gurobipy Implementation for Journal Committee Optimization Problem
"""

import gurobipy as gp
from gurobipy import GRB

def journal_committee_optimization():
    """Optimize editor assignments to maximize journal sales."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("journal_committee")
    
    # Data from the problem
    journals = [1, 2, 3]
    editors = [1, 2, 3]
    sales = {1: 1200.0, 2: 1800.0, 3: 900.0}
    max_workloads = {1: 4, 2: 3, 3: 5}
    
    # CRITICAL: Validate array lengths before loops
    assert len(journals) == len(sales), "Journal and sales length mismatch"
    assert len(editors) == len(max_workloads), "Editor and workload length mismatch"
    
    # 2. VARIABLES
    # Binary decision variables x_ej: 1 if editor e is assigned to journal j, 0 otherwise
    x = model.addVars(editors, journals, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total sales: sum over all e and j of sales_j * x_ej
    model.setObjective(gp.quicksum(sales[j] * x[e, j] for e in editors for j in journals), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    
    # Editor Workload Constraint: Each editor e cannot be assigned more journals than their max workload
    for e in editors:
        model.addConstr(gp.quicksum(x[e, j] for j in journals) <= max_workloads[e], name=f"workload_{e}")
    
    # Journal Assignment Constraint: Each journal j must be assigned at least one editor
    for j in journals:
        model.addConstr(gp.quicksum(x[e, j] for e in editors) >= 1, name=f"assignment_{j}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for e in editors:
            for j in journals:
                if x[e, j].x > 0.5:  # Check if x_ej is 1 (assigned)
                    print(f"Editor {e} assigned to Journal {j}")
    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__":
    journal_committee_optimization()
```

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

## 6. DOCplex Implementation

```python
#!/usr/bin/env python3
"""
DOCplex implementation for journal committee assignment problem
"""

from docplex.mp.model import Model

def journal_committee_optimization():
    """Optimize journal assignments to maximize sales while respecting editor workloads."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="journal_committee")
    
    # Data from the problem
    journal_sales = [1200.0, 1800.0, 900.0]
    editor_workloads = [4, 3, 5]
    
    # Number of journals and editors
    n_journals = len(journal_sales)
    n_editors = len(editor_workloads)
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert n_journals > 0 and n_editors > 0, "Array length mismatch or empty arrays"
    
    # Safe indexing range
    safe_journals = range(n_journals)
    safe_editors = range(n_editors)
    
    # 2. VARIABLES
    # Binary decision variables: x_ej = 1 if editor e is assigned to journal j
    x = {(e, j): mdl.binary_var(name=f"x_{e}_{j}") for e in safe_editors for j in safe_journals}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total sales: sum(Sales_j * x_ej) for all e, j
    total_sales = mdl.sum(journal_sales[j] * x[e, j] for e in safe_editors for j in safe_journals)
    mdl.maximize(total_sales)
    
    # 4. CONSTRAINTS
    
    # Constraint 1: Editor workload limit
    for e in safe_editors:
        workload = mdl.sum(x[e, j] for j in safe_journals)
        mdl.add_constraint(workload <= editor_workloads[e], ctname=f"workload_{e}")
    
    # Constraint 2: Each journal must have at least one editor
    for j in safe_journals:
        assignment = mdl.sum(x[e, j] for e in safe_editors)
        mdl.add_constraint(assignment >= 1, ctname=f"assignment_{j}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for e in safe_editors:
            for j in safe_journals:
                if solution.get_value(x[e, j]) > 0.5:  # Check if assigned
                    print(f"Editor {e} assigned to Journal {j}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

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

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 11700.0
**Execution Time**: 1.13 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 Journal Committee Optimization Problem
"""

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

def journal_committee_optimization():
    """Optimize the assignment of editors to journals to maximize total sales."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Journals and their sales
    journals = {1: 1200.0, 2: 1800.0, 3: 900.0}
    
    # Editors and their maximum workloads
    editors = {1: 4, 2: 3, 3: 5}
    
    # Validate array lengths
    assert len(journals) == 3, "Number of journals mismatch"
    assert len(editors) == 3, "Number of editors mismatch"
    
    # 3. SETS
    model.J = pyo.Set(initialize=journals.keys())  # Journals
    model.E = pyo.Set(initialize=editors.keys())   # Editors
    
    # 4. PARAMETERS
    model.Sales = pyo.Param(model.J, initialize=journals)
    model.Max_Workload = pyo.Param(model.E, initialize=editors)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.E, model.J, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.Sales[j] * model.x[e, j] for e in model.E for j in model.J)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Editor Workload Constraint
    def workload_rule(model, e):
        return sum(model.x[e, j] for j in model.J) <= model.Max_Workload[e]
    model.workload_constraint = pyo.Constraint(model.E, rule=workload_rule)
    
    # Journal Assignment Constraint
    def assignment_rule(model, j):
        return sum(model.x[e, j] for e in model.E) >= 1
    model.assignment_constraint = pyo.Constraint(model.J, rule=assignment_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("\nAssignment details:")
        for e in model.E:
            for j in model.J:
                if pyo.value(model.x[e, j]) > 0.5:  # Only print assignments
                    print(f"Editor {e} assigned to Journal {j}")
        
    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__":
    journal_committee_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 11700.0
**Execution Time**: 0.86 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 | 11700.00 | 0.20s | N/A | N/A |
| Docplex | OPTIMAL | 11700.00 | 1.13s | N/A | N/A |
| Pyomo | OPTIMAL | 11700.00 | 0.86s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 11700.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is recommended due to its fastest execution time while still providing the optimal solution.

### Business Interpretation
**Overall Strategy**: The optimal total sales of journals is $11,700, achieved by assigning editors to journals in a way that maximizes sales while respecting workload constraints.
**Objective Value Meaning**: The optimal objective value of $11,700 represents the maximum total sales achievable given the constraints on editor workloads and the requirement that each journal must have at least one editor.
**Resource Allocation Summary**: Editors should be assigned to journals in a way that maximizes sales while ensuring no editor exceeds their maximum workload and each journal has at least one editor.
**Implementation Recommendations**: 1. Assign editors to journals based on the optimal solution. 2. Monitor editor workloads to ensure they do not exceed their limits. 3. Regularly review sales data and editor assignments to adjust for any changes in sales figures or editor availability.