# Complete Optimization Problem and Solution: icfp_1

## 1. Problem Context and Goals

### Context  
The conference is organizing a series of paper presentations and aims to optimize the scheduling process. The primary decision involves determining whether a paper is scheduled in a particular session, represented by a binary decision variable. The objective is to minimize the total number of sessions required to present all papers. Each session can accommodate a limited number of papers, and no author should present more than one paper in the same session. The business configuration includes a parameter specifying the maximum number of papers that can be scheduled in a single session, which serves as a constraint in the optimization model. The focus is on making precise operational decisions that align with linear formulations, ensuring resource limitations are respected without involving nonlinear relationships.

### Goals  
The optimization goal is to minimize the total number of sessions needed for the conference. The metric to optimize is the total number of sessions, which is directly related to the decision of scheduling papers into sessions. Success is measured by achieving the minimum possible number of sessions while adhering to the constraints. The goal is articulated in natural language to ensure clarity and alignment with the linear optimization objective.

## 2. Constraints    

The constraints for the scheduling problem are as follows:

- Each paper must be scheduled in exactly one session. This ensures that every paper is presented once during the conference.
- The number of papers scheduled in any session cannot exceed the maximum number of papers allowed per session. This constraint ensures that sessions are not overloaded beyond their capacity.
- An author cannot present more than one paper in the same session. This constraint prevents scheduling conflicts for authors who have multiple papers.

These constraints are described in business terms that naturally lead to linear mathematical forms, avoiding any nonlinear relationships.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include creating tables for decision variables and author-paper associations, and updating configuration logic for session constraints.

CREATE TABLE Papers (
  paper_id INTEGER,
  title STRING
);

CREATE TABLE Authors (
  author_id INTEGER,
  name STRING
);

CREATE TABLE AuthorPaperAssociations (
  author_id INTEGER,
  paper_id INTEGER
);

CREATE TABLE Sessions (
  session_id INTEGER,
  paper_id INTEGER
);
```

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

- **Papers Table**: Stores information about each paper, including a unique identifier and the title. This data is crucial for identifying papers in the scheduling decision variables.
  
- **Authors Table**: Contains details about each author, including a unique identifier and name. This information is used to ensure that authors do not have scheduling conflicts.

- **AuthorPaperAssociations Table**: Links authors to their respective papers, facilitating the enforcement of constraints related to author scheduling.

- **Sessions Table**: Represents the scheduling of papers into sessions, with each entry indicating a paper scheduled in a specific session. This table is central to the decision-making process for paper scheduling.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical conference settings, ensuring a balance between the number of papers, authors, and sessions to create a realistic scheduling scenario.

-- Realistic data for Papers
INSERT INTO Papers (paper_id, title) VALUES (1, 'Advances in AI');
INSERT INTO Papers (paper_id, title) VALUES (2, 'Quantum Computing');
INSERT INTO Papers (paper_id, title) VALUES (3, 'Blockchain Technology');

-- Realistic data for Authors
INSERT INTO Authors (author_id, name) VALUES (1, 'Dr. Alice Smith');
INSERT INTO Authors (author_id, name) VALUES (2, 'Dr. Bob Johnson');
INSERT INTO Authors (author_id, name) VALUES (3, 'Dr. Carol Williams');

-- Realistic data for AuthorPaperAssociations
INSERT INTO AuthorPaperAssociations (author_id, paper_id) VALUES (1, 1);
INSERT INTO AuthorPaperAssociations (author_id, paper_id) VALUES (2, 2);
INSERT INTO AuthorPaperAssociations (author_id, paper_id) VALUES (3, 3);

-- Realistic data for Sessions
INSERT INTO Sessions (session_id, paper_id) VALUES (1, 1);
INSERT INTO Sessions (session_id, paper_id) VALUES (2, 2);
INSERT INTO Sessions (session_id, paper_id) VALUES (3, 3);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_{ij} \) be a binary decision variable where \( x_{ij} = 1 \) if paper \( i \) is scheduled in session \( j \), and \( x_{ij} = 0 \) otherwise.

#### Objective Function
- Minimize the total number of sessions used:
  \[
  \text{Minimize} \quad \sum_{j} y_j
  \]
  where \( y_j \) is a binary variable that equals 1 if session \( j \) is used, and 0 otherwise.

#### Constraints
1. **Each paper must be scheduled in exactly one session:**
   \[
   \sum_{j} x_{ij} = 1 \quad \forall i \in \text{Papers}
   \]
   - **Data Source Verification:** Papers.paper_id

2. **The number of papers scheduled in any session cannot exceed the maximum number of papers allowed per session:**
   \[
   \sum_{i} x_{ij} \leq \text{max\_papers\_per\_session} \times y_j \quad \forall j
   \]
   - **Data Source Verification:** Business configuration parameter for max\_papers\_per\_session

3. **An author cannot present more than one paper in the same session:**
   \[
   \sum_{i \in A_k} x_{ij} \leq 1 \quad \forall j, \forall k \in \text{Authors}
   \]
   where \( A_k \) is the set of papers authored by author \( k \).
   - **Data Source Verification:** AuthorPaperAssociations.author_id, AuthorPaperAssociations.paper_id

4. **Linking sessions to papers:**
   \[
   x_{ij} \leq y_j \quad \forall i, \forall j
   \]
   - **Data Source Verification:** Sessions.session_id, Sessions.paper_id

#### Additional Notes
- The binary variable \( y_j \) ensures that a session is counted as used only if at least one paper is scheduled in it.
- The constraints ensure that each paper is scheduled exactly once, no session exceeds its capacity, and no author presents more than one paper per session.

This formulation is designed to be immediately solvable using linear programming or mixed-integer programming solvers, adhering strictly to the linear constraints and objective function requirements.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_conference_schedule():
    """Optimize the scheduling of conference papers into sessions."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("conference_schedule")
    
    # Data: Example setup based on the provided schema and data
    papers = [1, 2, 3]  # Paper IDs
    authors = [1, 2, 3]  # Author IDs
    max_papers_per_session = 2  # Business configuration parameter
    
    # Author-paper associations
    author_paper_map = {
        1: [1],  # Author 1 has Paper 1
        2: [2],  # Author 2 has Paper 2
        3: [3]   # Author 3 has Paper 3
    }
    
    # Sessions (initially empty, to be determined by the optimization)
    sessions = range(1, len(papers) + 1)  # Assume at most one session per paper initially
    
    # 2. VARIABLES
    # Decision variables: x[i, j] = 1 if paper i is scheduled in session j
    x = model.addVars(papers, sessions, vtype=GRB.BINARY, name="x")
    
    # Session usage variables: y[j] = 1 if session j is used
    y = model.addVars(sessions, vtype=GRB.BINARY, name="y")
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the total number of sessions used
    model.setObjective(gp.quicksum(y[j] for j in sessions), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS
    
    # Each paper must be scheduled in exactly one session
    model.addConstrs((gp.quicksum(x[i, j] for j in sessions) == 1 for i in papers), name="one_session_per_paper")
    
    # The number of papers in any session cannot exceed the maximum allowed
    model.addConstrs((gp.quicksum(x[i, j] for i in papers) <= max_papers_per_session * y[j] for j in sessions), name="session_capacity")
    
    # An author cannot present more than one paper in the same session
    for k in authors:
        papers_by_author = author_paper_map.get(k, [])
        model.addConstrs((gp.quicksum(x[i, j] for i in papers_by_author) <= 1 for j in sessions), name=f"author_{k}_conflict")
    
    # Linking sessions to papers
    model.addConstrs((x[i, j] <= y[j] for i in papers for j in sessions), name="link_sessions")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for j in sessions:
            if y[j].x > 0.5:  # Session is used
                scheduled_papers = [i for i in papers if x[i, j].x > 0.5]
                print(f"Session {j} includes papers: {scheduled_papers}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_conference_schedule()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 2.0
**Execution Time**: 0.76 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with the shortest execution time, indicating efficient performance and reliability.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def conference_scheduling_optimization():
    """Optimize conference paper scheduling to minimize the number of sessions."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="conference_scheduling")
    
    # Data: Papers, Authors, and Author-Paper Associations
    papers = [1, 2, 3]  # Paper IDs
    authors = [1, 2, 3]  # Author IDs
    author_paper_associations = {
        1: [1],  # Author 1 has Paper 1
        2: [2],  # Author 2 has Paper 2
        3: [3]   # Author 3 has Paper 3
    }
    
    max_papers_per_session = 2  # Business configuration parameter
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(papers) == len(authors) == len(author_paper_associations), "Array length mismatch"
    safe_range_papers = range(len(papers))
    safe_range_authors = range(len(authors))
    
    # 2. VARIABLES
    # Decision variables
    x = {(i, j): mdl.binary_var(name=f"x_{i}_{j}") for i in papers for j in papers}
    y = {j: mdl.binary_var(name=f"y_{j}") for j in papers}
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the total number of sessions used
    mdl.minimize(mdl.sum(y[j] for j in papers))
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Each paper must be scheduled in exactly one session
    for i in papers:
        mdl.add_constraint(mdl.sum(x[i, j] for j in papers) == 1, ctname=f"one_session_per_paper_{i}")
    
    # The number of papers scheduled in any session cannot exceed the maximum number of papers allowed per session
    for j in papers:
        mdl.add_constraint(mdl.sum(x[i, j] for i in papers) <= max_papers_per_session * y[j], ctname=f"max_papers_per_session_{j}")
    
    # An author cannot present more than one paper in the same session
    for j in papers:
        for k in authors:
            mdl.add_constraint(mdl.sum(x[i, j] for i in author_paper_associations[k]) <= 1, ctname=f"one_paper_per_author_per_session_{j}_{k}")
    
    # Linking sessions to papers
    for i in papers:
        for j in papers:
            mdl.add_constraint(x[i, j] <= y[j], ctname=f"link_sessions_to_papers_{i}_{j}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in papers:
            for j in papers:
                value = solution.get_value(x[i, j])
                if value > 1e-6:
                    print(f"x[{i},{j}] = {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
conference_scheduling_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 2.0
**Execution Time**: 5.61 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution, albeit with a longer execution time compared to Gurobipy, but still reliable.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def conference_scheduling_optimization():
    """Optimize conference paper scheduling using Pyomo"""

    # 1. MODEL CREATION
    model = pyo.ConcreteModel()

    # 2. DATA SETUP
    papers = [1, 2, 3]  # Paper IDs
    authors = [1, 2, 3]  # Author IDs
    max_papers_per_session = 2  # Maximum papers per session

    # Author-paper associations
    author_paper_map = {
        1: [1],
        2: [2],
        3: [3]
    }

    # 3. SETS
    model.P = pyo.Set(initialize=papers)  # Papers
    model.A = pyo.Set(initialize=authors)  # Authors
    model.S = pyo.RangeSet(1, len(papers))  # Sessions (at most one session per paper)

    # 4. VARIABLES
    model.x = pyo.Var(model.P, model.S, within=pyo.Binary)  # Paper-session assignment
    model.y = pyo.Var(model.S, within=pyo.Binary)  # Session usage

    # 5. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.y[j] for j in model.S)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.minimize)

    # 6. CONSTRAINTS

    # Each paper must be scheduled in exactly one session
    def paper_scheduling_rule(model, i):
        return sum(model.x[i, j] for j in model.S) == 1
    model.paper_scheduling_constraint = pyo.Constraint(model.P, rule=paper_scheduling_rule)

    # The number of papers in any session cannot exceed the maximum allowed
    def session_capacity_rule(model, j):
        return sum(model.x[i, j] for i in model.P) <= max_papers_per_session * model.y[j]
    model.session_capacity_constraint = pyo.Constraint(model.S, rule=session_capacity_rule)

    # An author cannot present more than one paper in the same session
    def author_conflict_rule(model, j, k):
        return sum(model.x[i, j] for i in author_paper_map[k]) <= 1
    model.author_conflict_constraint = pyo.Constraint(model.S, model.A, rule=author_conflict_rule)

    # Linking sessions to papers
    def linking_rule(model, i, j):
        return model.x[i, j] <= model.y[j]
    model.linking_constraint = pyo.Constraint(model.P, model.S, rule=linking_rule)

    # 7. SOLVING WITH GUROBI
    solver = SolverFactory('gurobi')

    # Solve the model
    results = solver.solve(model, tee=True)

    # 8. RESULT PROCESSING
    if results.solver.termination_condition == pyo.TerminationCondition.optimal:
        print("Optimal solution found!")
        print(f"Optimal value: {pyo.value(model.objective)}")

        # Extract variable values
        print("\nPaper-session assignments:")
        for i in model.P:
            for j in model.S:
                if pyo.value(model.x[i, j]) > 0.5:  # Binary variable check
                    print(f"Paper {i} is scheduled in session {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

# Run the optimization
conference_scheduling_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 2.0
**Execution Time**: 1.58 seconds
**Reliability**: high
**Analysis**: Pyomo achieved an optimal solution with a moderate execution time, demonstrating reliability and consistency with other solvers.

## 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.76s | N/A | N/A |
| Docplex | OPTIMAL | 2.00 | 5.61s | N/A | N/A |
| Pyomo | OPTIMAL | 2.00 | 1.58s | 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 fastest execution time and high reliability, making it suitable for scenarios requiring quick decision-making.

### Business Interpretation
**Overall Strategy**: The optimal solution indicates that the minimum number of sessions required is 2, which efficiently utilizes resources while adhering to constraints.
**Objective Value Meaning**: The optimal objective value of 2 means that only two sessions are needed to accommodate all papers while satisfying all constraints.
**Resource Allocation Summary**: Allocate resources to two sessions, ensuring no session exceeds its capacity and authors do not present more than one paper per session.
**Implementation Recommendations**: Implement the solution by scheduling papers into two sessions, verifying that all constraints are met, and using Gurobipy for any future optimization needs due to its efficiency.