## 4. Mathematical Optimization Formulation

#### Decision Variables
Let \( x_{c,y} \) be the number of weddings held at church \( c \) in year \( y \), where:
- \( c \in \{1, 2, 3\} \) (church IDs)
- \( y \in \{2023, 2024\} \) (years)

#### Objective Function
Minimize the total cost of organizing weddings across all churches and years:
\[
\text{Minimize } \sum_{c} \sum_{y} \text{cost\_per\_wedding.cost}_{c,y} \times x_{c,y}
\]
Where:
- \( \text{cost\_per\_wedding.cost}_{c,y} \) is the cost of organizing a wedding at church \( c \) in year \( y \).

#### Constraints
1. **Annual Wedding Limit**: The total number of weddings organized across all churches in a given year must not exceed 120:
\[
\sum_{c} x_{c,y} \leq 120 \quad \forall y
\]
2. **Church Capacity Limit**: The number of weddings held at each church in a given year must not exceed 30:
\[
x_{c,y} \leq 30 \quad \forall c, y
\]
3. **Non-Negativity**: The number of weddings cannot be negative:
\[
x_{c,y} \geq 0 \quad \forall c, y
\]

#### Data Source Verification
- **Objective Function Coefficients**: \( \text{cost\_per\_wedding.cost}_{c,y} \) comes from the `cost_per_wedding` table.
- **Annual Wedding Limit**: The constant 120 is a business configuration parameter.
- **Church Capacity Limit**: The constant 30 is a business configuration parameter.

#### Complete Numerical Model
Using the provided data, the numerical model is as follows:

**Objective Function**:
\[
\text{Minimize } 5000x_{1,2023} + 4500x_{2,2023} + 6000x_{3,2023} + 5200x_{1,2024} + 4600x_{2,2024} + 6100x_{3,2024}
\]

**Constraints**:
1. **Annual Wedding Limit**:
\[
x_{1,2023} + x_{2,2023} + x_{3,2023} \leq 120
\]
\[
x_{1,2024} + x_{2,2024} + x_{3,2024} \leq 120
\]
2. **Church Capacity Limit**:
\[
x_{1,2023} \leq 30, \quad x_{2,2023} \leq 30, \quad x_{3,2023} \leq 30
\]
\[
x_{1,2024} \leq 30, \quad x_{2,2024} \leq 30, \quad x_{3,2024} \leq 30
\]
3. **Non-Negativity**:
\[
x_{1,2023} \geq 0, \quad x_{2,2023} \geq 0, \quad x_{3,2023} \geq 0
\]
\[
x_{1,2024} \geq 0, \quad x_{2,2024} \geq 0, \quad x_{3,2024} \geq 0
\]

This is a complete, immediately solvable Linear Programming (LP) model.

## 5. Gurobipy Implementation

```python
#!/usr/bin/env python3
"""
Gurobipy 12.0.2 Implementation for Wedding Optimization Problem
"""

import gurobipy as gp
from gurobipy import GRB

def wedding_optimization():
    """Optimize the number of weddings at each church to minimize total cost."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("wedding_optimization")
    
    # Data from the problem
    churches = [1, 2, 3]
    years = [2023, 2024]
    
    # Cost per wedding at each church and year
    cost_per_wedding = {
        (1, 2023): 5000.0,
        (2, 2023): 4500.0,
        (3, 2023): 6000.0,
        (1, 2024): 5200.0,
        (2, 2024): 4600.0,
        (3, 2024): 6100.0
    }
    
    # Maximum number of weddings per year
    max_weddings_per_year = 120
    
    # Maximum number of weddings per church per year
    max_weddings_per_church = 30
    
    # CRITICAL: Validate data consistency
    assert len(churches) == 3, "Expected 3 churches"
    assert len(years) == 2, "Expected 2 years"
    assert len(cost_per_wedding) == 6, "Expected 6 cost entries"
    
    # 2. VARIABLES
    # Decision variables: number of weddings at each church each year
    x = model.addVars(churches, years, vtype=GRB.INTEGER, name="x", lb=0)
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the total cost of weddings
    model.setObjective(
        gp.quicksum(cost_per_wedding[c, y] * x[c, y] for c in churches for y in years),
        GRB.MINIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # Annual Wedding Limit
    for y in years:
        model.addConstr(
            gp.quicksum(x[c, y] for c in churches) <= max_weddings_per_year,
            name=f"annual_limit_{y}"
        )
    
    # Church Capacity Limit
    for c in churches:
        for y in years:
            model.addConstr(
                x[c, y] <= max_weddings_per_church,
                name=f"church_capacity_{c}_{y}"
            )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for c in churches:
            for y in years:
                if x[c, y].x > 1e-6:
                    print(f"Weddings at church {c} in year {y}: {x[c, y].x:.0f}")
    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__":
    wedding_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 0.0
**Execution Time**: 0.18 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution with an objective value of 0.0, indicating no weddings are scheduled. This is likely due to the solver interpreting the problem as having no feasible solution or an error in the model setup.

## 6. DOCplex Implementation

```python
#!/usr/bin/env python3
"""
DOCplex 2.29.245 Implementation for Wedding Optimization Problem
"""

from docplex.mp.model import Model

def wedding_optimization():
    """Optimize the number of weddings to minimize total cost while respecting constraints."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="wedding_optimization")
    
    # Data from the problem
    churches = [1, 2, 3]
    years = [2023, 2024]
    
    # Cost per wedding at each church and year
    cost_per_wedding = {
        (1, 2023): 5000.0,
        (2, 2023): 4500.0,
        (3, 2023): 6000.0,
        (1, 2024): 5200.0,
        (2, 2024): 4600.0,
        (3, 2024): 6100.0
    }
    
    # Maximum number of weddings per year
    max_weddings_per_year = 120
    
    # Maximum number of weddings per church per year
    max_weddings_per_church = 30
    
    # 2. VARIABLES
    # Decision variables: number of weddings at each church each year
    x = {(c, y): mdl.continuous_var(name=f"x_{c}_{y}", lb=0) for c in churches for y in years}
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the total cost of organizing weddings
    total_cost = mdl.sum(cost_per_wedding[c, y] * x[c, y] for c in churches for y in years)
    mdl.minimize(total_cost)
    
    # 4. CONSTRAINTS
    
    # Annual Wedding Limit: Total weddings per year <= 120
    for y in years:
        total_weddings = mdl.sum(x[c, y] for c in churches)
        mdl.add_constraint(total_weddings <= max_weddings_per_year, ctname=f"annual_limit_{y}")
    
    # Church Capacity Limit: Weddings per church per year <= 30
    for c in churches:
        for y in years:
            mdl.add_constraint(x[c, y] <= max_weddings_per_church, ctname=f"church_capacity_{c}_{y}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for c in churches:
            for y in years:
                value = solution.get_value(x[c, y])
                if value > 1e-6:
                    print(f"Weddings at church {c} in year {y}: {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

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

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 0.0
**Execution Time**: 1.06 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution with an objective value of 0.0, consistent with Gurobipy. This suggests a potential issue with the model formulation or data.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def wedding_optimization():
    """Optimize the number of weddings to minimize total cost while respecting constraints."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    churches = [1, 2, 3]
    years = [2023, 2024]
    
    # Cost per wedding data
    cost_data = {
        (1, 2023): 5000.0,
        (2, 2023): 4500.0,
        (3, 2023): 6000.0,
        (1, 2024): 5200.0,
        (2, 2024): 4600.0,
        (3, 2024): 6100.0
    }
    
    # Validate data lengths
    assert len(cost_data) == len(churches) * len(years), "Cost data length mismatch"
    
    # 3. SETS
    model.C = pyo.Set(initialize=churches)  # Churches
    model.Y = pyo.Set(initialize=years)    # Years
    
    # 4. PARAMETERS
    model.cost = pyo.Param(model.C, model.Y, initialize=cost_data)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.C, model.Y, within=pyo.NonNegativeIntegers, bounds=(0, 30))
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.cost[c, y] * model.x[c, y] for c in model.C for y in model.Y)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.minimize)
    
    # 7. CONSTRAINTS
    
    # Annual Wedding Limit
    def annual_limit_rule(model, y):
        return sum(model.x[c, y] for c in model.C) <= 120
    model.annual_limit = pyo.Constraint(model.Y, rule=annual_limit_rule)
    
    # Church Capacity Limit (already enforced by variable bounds)
    
    # 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)}")
        
        # Extract variable values
        print("\nVariable values:")
        for c in model.C:
            for y in model.Y:
                x_val = pyo.value(model.x[c, y])
                if x_val > 1e-6:  # Only print non-zero values
                    print(f"x[{c}, {y}] = {x_val:.0f}")
        
    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__":
    wedding_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 0.0
**Execution Time**: 0.99 seconds
**Reliability**: high
**Analysis**: Pyomo returned the same optimal value of 0.0, further confirming the consistency across solvers. This reinforces the likelihood of a problem with the model or data.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 0.00 | 0.18s | N/A | N/A |
| Docplex | OPTIMAL | 0.00 | 1.06s | N/A | N/A |
| Pyomo | OPTIMAL | 0.00 | 0.99s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 0.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: multiple
**Reasoning**: All solvers returned consistent results, indicating a systemic issue with the model rather than a solver-specific problem. Multiple solvers were used to ensure reliability.

### Business Interpretation
**Overall Strategy**: The optimal solution suggests no weddings should be scheduled, which is likely not the intended outcome. This indicates a need to revisit the model formulation or data inputs.
**Objective Value Meaning**: The optimal objective value of 0.0 implies no weddings are scheduled, which is not practical. This suggests a need to re-examine the model constraints or data.
**Resource Allocation Summary**: No resources are allocated to weddings, indicating a potential issue with the model or constraints.
**Implementation Recommendations**: Review the model formulation, constraints, and data inputs to ensure they align with the business requirements. Verify the feasibility of the constraints and the correctness of the objective function coefficients.