# Complete Optimization Problem and Solution: wedding

## 1. Problem Context and Goals

### Context  
The objective is to optimize the allocation of weddings to churches in a manner that maximizes the total number of weddings held. This involves making decisions about which church will host each wedding, with the decision variables being whether a wedding is allocated to a specific church with a particular male and female participant. The operational parameters are aligned with the goal of maximizing the total number of weddings, which is achieved by summing the allocations across all churches and participants.

Each church has a maximum number of weddings it can host per year, which serves as a constraint in the optimization model. Additionally, business logic ensures that each participant, whether male or female, is involved in at most one wedding. This is expressed in natural language as a constraint that limits each participant to a single wedding engagement.

The data presented reflects current operational information, focusing on precise decision-making that leads to a linear formulation. Resource limitations, such as church capacity and participant availability, are considered as linear constraints. The business configuration includes both scalar parameters, like the maximum number of weddings a church can host, and business logic formulas that ensure unique participation.

### Goals  
The primary goal of this optimization problem is to maximize the total number of weddings held across all available churches. The metric to optimize is the total number of weddings, which is calculated by summing the allocations of weddings to churches, ensuring that each wedding involves one male and one female participant. Success is measured by the ability to maximize this total while adhering to the constraints of church capacity and unique participant involvement. The optimization goal is described in natural language, focusing on maximizing the number of weddings without using mathematical formulas or symbolic notation.

## 2. Constraints    

The constraints for this optimization problem are designed to ensure that the allocation of weddings adheres to the operational limitations of the churches and the unique participation of individuals:

- Each church can host a limited number of weddings per year, which is determined by its capacity. This constraint ensures that the total number of weddings allocated to a church does not exceed its capacity.
- Each male participant can be involved in at most one wedding. This constraint ensures that no male participant is allocated to more than one wedding.
- Each female participant can be involved in at most one wedding. This constraint ensures that no female participant is allocated to more than one wedding.

These constraints are described in business terms that naturally lead to linear mathematical forms, avoiding any nonlinear relationships such as variable products or divisions.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include creating new tables for church capacities and participant constraints, modifying existing tables to improve mapping adequacy, and updating configuration logic for scalar parameters and formulas.

CREATE TABLE wedding (
  Church_ID INTEGER,
  Male_ID INTEGER,
  Female_ID INTEGER,
  total_weddings INTEGER
);

CREATE TABLE ChurchCapacity (
  church_id INTEGER,
  capacity INTEGER
);

CREATE TABLE ParticipantConstraints (
  participant_id INTEGER,
  constraint_type STRING
);
```

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

- **Wedding Table**: This table stores information about weddings held at churches. It plays a crucial role in the optimization as it contains the decision variables for allocating weddings. The columns include:
  - **Church_ID**: Identifies the church where the wedding is held. It is used to allocate weddings to specific churches.
  - **Male_ID**: Identifies the male participant in the wedding. It ensures unique male participation.
  - **Female_ID**: Identifies the female participant in the wedding. It ensures unique female participation.
  - **Total_Weddings**: Represents the total number of weddings held, serving as the objective coefficient for maximizing weddings.

- **ChurchCapacity Table**: This table stores the capacity limits for each church, which are used as constraint bounds in the optimization model. The columns include:
  - **Church_ID**: Identifies the church and links to the church capacity constraints.
  - **Capacity**: Indicates the maximum number of weddings a church can host, serving as a constraint bound.

- **ParticipantConstraints Table**: This table ensures the unique participation of individuals in weddings. The columns include:
  - **Participant_ID**: Identifies the participant, ensuring unique participation.
  - **Constraint_Type**: Defines the type of participation constraint, such as male or female.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical church capacities and participant availability to ensure a realistic and solvable optimization problem.

-- Realistic data for wedding
INSERT INTO wedding (Church_ID, Male_ID, Female_ID, total_weddings) VALUES (1, 101, 201, 10);
INSERT INTO wedding (Church_ID, Male_ID, Female_ID, total_weddings) VALUES (2, 102, 202, 15);
INSERT INTO wedding (Church_ID, Male_ID, Female_ID, total_weddings) VALUES (3, 103, 203, 20);

-- Realistic data for ChurchCapacity
INSERT INTO ChurchCapacity (church_id, capacity) VALUES (1, 100);
INSERT INTO ChurchCapacity (church_id, capacity) VALUES (2, 150);
INSERT INTO ChurchCapacity (church_id, capacity) VALUES (3, 200);

-- Realistic data for ParticipantConstraints
INSERT INTO ParticipantConstraints (participant_id, constraint_type) VALUES (101, 'male');
INSERT INTO ParticipantConstraints (participant_id, constraint_type) VALUES (102, 'male');
INSERT INTO ParticipantConstraints (participant_id, constraint_type) VALUES (103, 'male');
INSERT INTO ParticipantConstraints (participant_id, constraint_type) VALUES (201, 'female');
INSERT INTO ParticipantConstraints (participant_id, constraint_type) VALUES (202, 'female');
INSERT INTO ParticipantConstraints (participant_id, constraint_type) VALUES (203, 'female');
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- \( x_{ijk} \): Binary variable that equals 1 if wedding \( k \) is held at church \( i \) with male participant \( j \) and female participant \( k \), and 0 otherwise.

#### Objective Function
Maximize the total number of weddings held:
\[ \text{Maximize} \quad \sum_{i,j,k} x_{ijk} \]

#### Constraints
1. **Church Capacity Constraints**: Ensure that the number of weddings at each church does not exceed its capacity.
   \[
   \sum_{j,k} x_{ijk} \leq \text{capacity}_i \quad \forall i
   \]
   - Data Source Verification: `ChurchCapacity.capacity`

2. **Unique Male Participation**: Each male participant can be involved in at most one wedding.
   \[
   \sum_{i,k} x_{ijk} \leq 1 \quad \forall j \text{ where } \text{ParticipantConstraints.constraint_type} = \text{'male'}
   \]
   - Data Source Verification: `ParticipantConstraints.participant_id`

3. **Unique Female Participation**: Each female participant can be involved in at most one wedding.
   \[
   \sum_{i,j} x_{ijk} \leq 1 \quad \forall k \text{ where } \text{ParticipantConstraints.constraint_type} = \text{'female'}
   \]
   - Data Source Verification: `ParticipantConstraints.participant_id`

#### Data Source Verification
- The coefficients for the church capacity constraints are derived from the `ChurchCapacity.capacity` column.
- The constraints for unique male and female participation are based on the `ParticipantConstraints.participant_id` and `ParticipantConstraints.constraint_type` columns.

This formulation provides a complete and immediately solvable linear mathematical model, ensuring that all constraints and the objective function are linear and adhere to the problem's requirements.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_weddings():
    """Optimize the allocation of weddings to churches."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("wedding_optimization")
    
    # Data: Church capacities and participant constraints
    church_ids = [1, 2, 3]
    male_ids = [101, 102, 103]
    female_ids = [201, 202, 203]
    capacities = {1: 100, 2: 150, 3: 200}
    
    # Validate data lengths
    assert len(church_ids) == len(capacities), "Church data length mismatch"
    assert len(male_ids) == len(female_ids), "Participant data length mismatch"
    
    # 2. VARIABLES
    # Decision variables: x[i,j,k] = 1 if wedding with male j and female k is held at church i
    x = model.addVars(church_ids, male_ids, female_ids, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total number of weddings
    model.setObjective(gp.quicksum(x[i, j, k] for i in church_ids for j in male_ids for k in female_ids), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    
    # Church capacity constraints
    model.addConstrs((gp.quicksum(x[i, j, k] for j in male_ids for k in female_ids) <= capacities[i] for i in church_ids), name="church_capacity")
    
    # Unique male participation
    model.addConstrs((gp.quicksum(x[i, j, k] for i in church_ids for k in female_ids) <= 1 for j in male_ids), name="unique_male")
    
    # Unique female participation
    model.addConstrs((gp.quicksum(x[i, j, k] for i in church_ids for j in male_ids) <= 1 for k in female_ids), name="unique_female")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in church_ids:
            for j in male_ids:
                for k in female_ids:
                    if x[i, j, k].x > 1e-6:
                        print(f"Wedding at Church {i} with Male {j} and Female {k}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_weddings()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 3.0
**Execution Time**: 1.07 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with a consistent optimal value of 3.0. It is known for its efficiency and reliability in solving linear optimization problems.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_weddings():
    """Optimize the allocation of weddings to churches."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="wedding_optimization")
    
    # Data: Church capacities
    church_ids = [1, 2, 3]
    capacities = [100, 150, 200]
    
    # Data: Participants
    male_ids = [101, 102, 103]
    female_ids = [201, 202, 203]
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(church_ids) == len(capacities), "Array length mismatch for church capacities"
    assert len(male_ids) == len(female_ids), "Array length mismatch for participants"
    
    # Safe indexing
    safe_church_range = range(len(church_ids))
    safe_participant_range = range(len(male_ids))
    
    # 2. VARIABLES
    # Decision variables: x[i][j][k] = 1 if wedding with male j and female k is held at church i
    x = {(i, j, k): mdl.binary_var(name=f"x_{i}_{j}_{k}") for i in safe_church_range for j in safe_participant_range for k in safe_participant_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total number of weddings
    objective = mdl.sum(x[i, j, k] for i in safe_church_range for j in safe_participant_range for k in safe_participant_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Church capacity constraints
    for i in safe_church_range:
        mdl.add_constraint(mdl.sum(x[i, j, k] for j in safe_participant_range for k in safe_participant_range) <= capacities[i], ctname=f"capacity_{i}")
    
    # Unique male participation
    for j in safe_participant_range:
        mdl.add_constraint(mdl.sum(x[i, j, k] for i in safe_church_range for k in safe_participant_range) <= 1, ctname=f"unique_male_{j}")
    
    # Unique female participation
    for k in safe_participant_range:
        mdl.add_constraint(mdl.sum(x[i, j, k] for i in safe_church_range for j in safe_participant_range) <= 1, ctname=f"unique_female_{k}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_church_range:
            for j in safe_participant_range:
                for k in safe_participant_range:
                    value = solution.get_value(x[i, j, k])
                    if value > 1e-6:
                        print(f"x[{i}][{j}][{k}] = {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
optimize_weddings()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 3.0
**Execution Time**: 6.71 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution with the same optimal value of 3.0, albeit with a longer execution time. It is a robust solver for linear problems, especially within the IBM ecosystem.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def wedding_optimization():
    """Optimize the allocation of weddings to churches"""

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

    # 2. DATA SETUP
    # Example data based on the provided schema and realistic data
    church_ids = [1, 2, 3]
    male_ids = [101, 102, 103]
    female_ids = [201, 202, 203]
    capacities = {1: 100, 2: 150, 3: 200}

    # 3. SETS
    model.I = pyo.Set(initialize=church_ids)
    model.J = pyo.Set(initialize=male_ids)
    model.K = pyo.Set(initialize=female_ids)

    # 4. PARAMETERS
    model.capacity = pyo.Param(model.I, initialize=capacities)

    # 5. VARIABLES
    model.x = pyo.Var(model.I, model.J, model.K, within=pyo.Binary)

    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.x[i, j, k] for i in model.I for j in model.J for k in model.K)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)

    # 7. CONSTRAINTS

    # Church Capacity Constraints
    def church_capacity_rule(model, i):
        return sum(model.x[i, j, k] for j in model.J for k in model.K) <= model.capacity[i]
    model.church_capacity_constraint = pyo.Constraint(model.I, rule=church_capacity_rule)

    # Unique Male Participation
    def unique_male_rule(model, j):
        return sum(model.x[i, j, k] for i in model.I for k in model.K) <= 1
    model.unique_male_constraint = pyo.Constraint(model.J, rule=unique_male_rule)

    # Unique Female Participation
    def unique_female_rule(model, k):
        return sum(model.x[i, j, k] for i in model.I for j in model.J) <= 1
    model.unique_female_constraint = pyo.Constraint(model.K, rule=unique_female_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)

    # 9. 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("\nVariable values:")
        for i in model.I:
            for j in model.J:
                for k in model.K:
                    x_val = pyo.value(model.x[i, j, k])
                    if x_val > 1e-6:  # Only print non-zero values
                        print(f"x[{i},{j},{k}] = {int(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

# Run the optimization
wedding_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 3.0
**Execution Time**: 2.65 seconds
**Reliability**: high
**Analysis**: Pyomo achieved an optimal solution with the same optimal value of 3.0. It is a versatile tool that integrates well with various solvers, providing flexibility in modeling.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 3.00 | 1.07s | N/A | N/A |
| Docplex | OPTIMAL | 3.00 | 6.71s | N/A | N/A |
| Pyomo | OPTIMAL | 3.00 | 2.65s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 3.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is recommended due to its faster execution time and high reliability, making it suitable for operational environments where efficiency is crucial.

### Business Interpretation
**Overall Strategy**: The optimal solution indicates that a maximum of 3 weddings can be held given the constraints. This maximizes the use of available resources while adhering to participant and church capacity constraints.
**Objective Value Meaning**: The optimal objective value of 3.0 means that the maximum number of weddings that can be scheduled without violating any constraints is 3.
**Resource Allocation Summary**: Resources should be allocated to ensure that 3 weddings are scheduled, with each church and participant being utilized according to the constraints.
**Implementation Recommendations**: Ensure that the scheduling system is updated to reflect the optimal assignments, and communicate with all participants and churches to confirm the arrangements.