# Complete Optimization Problem and Solution: party_people

## 1. Problem Context and Goals

### Context  
A political party is focused on maximizing its influence across various regions by strategically allocating its members to events. The party aims to ensure that each region is adequately represented while keeping the total cost of organizing these events within a predefined budget. The influence in each region is directly proportional to the number of members assigned to events in that region, and the cost is determined by the number of events organized. 

The party has a total budget of 150,000 units available for organizing events, and the cost of organizing a single event in any region is 6,000 units. Each region has specific constraints on the number of members that can be assigned, the minimum and maximum number of events that can be organized, and the minimum number of members that must be assigned. The impact of assigning a member to an event varies by region, as reflected by the influence coefficient for each region.

The key decisions involve determining the number of members to assign to events in each region and the number of events to organize in each region. These decisions must respect the budget, regional member availability, and event organization limits.

### Goals  
The primary goal is to maximize the party's overall influence across all regions. This is achieved by strategically assigning members to events in each region, weighted by the region-specific influence coefficient. Success is measured by the total influence generated, which is the sum of the influence coefficients multiplied by the number of members assigned in each region. The optimization ensures that the total cost of organizing events does not exceed the available budget and that all regional constraints on member assignments and event organization are satisfied.

## 2. Constraints  

The optimization problem is subject to the following constraints:  
1. **Budget Constraint**: The total cost of organizing events across all regions must not exceed the total budget of 150,000 units. The cost per event is 6,000 units.  
2. **Member Assignment Upper Bound**: The number of members assigned to events in each region cannot exceed the total number of members available in that region.  
3. **Member Assignment Lower Bound**: Each region must have at least a minimum number of members assigned to events.  
4. **Event Organization Upper Bound**: The number of events organized in each region cannot exceed the maximum number of events allowed in that region.  
5. **Event Organization Lower Bound**: Each region must organize at least a minimum number of events.  

These constraints ensure that the party's operations are feasible, respect resource limitations, and meet regional requirements.

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include creating new tables for decision variables and constraint bounds, and updating business configuration logic for scalar parameters and formulas.

CREATE TABLE region_members (
  region_id INTEGER,
  number_of_members INTEGER
);

CREATE TABLE region_events (
  region_id INTEGER,
  number_of_events INTEGER
);

CREATE TABLE region_constraints (
  region_id INTEGER,
  total_members_available INTEGER,
  max_events INTEGER,
  min_members INTEGER,
  min_events INTEGER,
  influence_coefficient FLOAT
);
```

### Data Dictionary  
- **region_members**:  
  - **region_id**: Unique identifier for the region.  
  - **number_of_members**: Number of members assigned to events in the region. This is a decision variable in the optimization.  

- **region_events**:  
  - **region_id**: Unique identifier for the region.  
  - **number_of_events**: Number of events organized in the region. This is a decision variable in the optimization.  

- **region_constraints**:  
  - **region_id**: Unique identifier for the region.  
  - **total_members_available**: Total number of members available in the region. This serves as an upper bound for member assignments.  
  - **max_events**: Maximum number of events that can be organized in the region. This serves as an upper bound for event organization.  
  - **min_members**: Minimum number of members that must be assigned to the region. This serves as a lower bound for member assignments.  
  - **min_events**: Minimum number of events that must be organized in the region. This serves as a lower bound for event organization.  
  - **influence_coefficient**: Impact of assigning a member to an event in the region. This is used as a coefficient in the objective function to measure influence.  

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on realistic political party operations, ensuring that the number of members, events, and constraints align with typical regional capacities and budgets. The influence coefficients were set to reflect varying regional importance.

-- Realistic data for region_members
INSERT INTO region_members (region_id, number_of_members) VALUES (1, 15);
INSERT INTO region_members (region_id, number_of_members) VALUES (2, 20);
INSERT INTO region_members (region_id, number_of_members) VALUES (3, 10);

-- Realistic data for region_events
INSERT INTO region_events (region_id, number_of_events) VALUES (1, 3);
INSERT INTO region_events (region_id, number_of_events) VALUES (2, 4);
INSERT INTO region_events (region_id, number_of_events) VALUES (3, 2);

-- Realistic data for region_constraints
INSERT INTO region_constraints (region_id, total_members_available, max_events, min_members, min_events, influence_coefficient) VALUES (1, 20, 5, 5, 1, 0.6);
INSERT INTO region_constraints (region_id, total_members_available, max_events, min_members, min_events, influence_coefficient) VALUES (2, 25, 6, 6, 2, 0.7);
INSERT INTO region_constraints (region_id, total_members_available, max_events, min_members, min_events, influence_coefficient) VALUES (3, 15, 4, 4, 1, 0.5);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- \( x_i \): Number of members assigned to events in region \( i \) (integer)
- \( y_i \): Number of events organized in region \( i \) (integer)

#### Objective Function
Maximize the total influence across all regions:
\[
\text{Maximize } Z = \sum_{i} (\text{influence\_coefficient}_i \times x_i)
\]
Data Source Verification: `region_constraints.influence_coefficient`

#### Constraints
1. **Budget Constraint**: The total cost of organizing events must not exceed the budget.
\[
\sum_{i} (6000 \times y_i) \leq 150000
\]
Data Source Verification: Business configuration (cost per event = 6000 units, total budget = 150000 units)

2. **Member Assignment Upper Bound**: The number of members assigned to events in each region cannot exceed the total number of members available in that region.
\[
x_i \leq \text{total\_members\_available}_i \quad \forall i
\]
Data Source Verification: `region_constraints.total_members_available`

3. **Member Assignment Lower Bound**: Each region must have at least a minimum number of members assigned to events.
\[
x_i \geq \text{min\_members}_i \quad \forall i
\]
Data Source Verification: `region_constraints.min_members`

4. **Event Organization Upper Bound**: The number of events organized in each region cannot exceed the maximum number of events allowed in that region.
\[
y_i \leq \text{max\_events}_i \quad \forall i
\]
Data Source Verification: `region_constraints.max_events`

5. **Event Organization Lower Bound**: Each region must organize at least a minimum number of events.
\[
y_i \geq \text{min\_events}_i \quad \forall i
\]
Data Source Verification: `region_constraints.min_events`

#### Complete Mathematical Model
\[
\text{Maximize } Z = 0.6x_1 + 0.7x_2 + 0.5x_3
\]
Subject to:
\[
6000y_1 + 6000y_2 + 6000y_3 \leq 150000
\]
\[
x_1 \leq 20, \quad x_2 \leq 25, \quad x_3 \leq 15
\]
\[
x_1 \geq 5, \quad x_2 \geq 6, \quad x_3 \geq 4
\]
\[
y_1 \leq 5, \quad y_2 \leq 6, \quad y_3 \leq 4
\]
\[
y_1 \geq 1, \quad y_2 \geq 2, \quad y_3 \geq 1
\]
\[
x_i, y_i \in \mathbb{Z}^+ \quad \forall i
\]

This is a Mixed-Integer Linear Programming (MIP) formulation with integer decision variables \( x_i \) and \( y_i \), ensuring alignment with the business requirements and constraints.

## 5. Gurobipy Implementation

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

import gurobipy as gp
from gurobipy import GRB

def optimize_party_people():
    """Optimize the allocation of party members to events across regions."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("party_people")
    
    # Data from the problem
    regions = [1, 2, 3]
    influence_coefficients = {1: 0.6, 2: 0.7, 3: 0.5}
    total_members_available = {1: 20, 2: 25, 3: 15}
    max_events = {1: 5, 2: 6, 3: 4}
    min_members = {1: 5, 2: 6, 3: 4}
    min_events = {1: 1, 2: 2, 3: 1}
    cost_per_event = 6000
    total_budget = 150000
    
    # CRITICAL: Validate array lengths before loops
    assert len(regions) == len(influence_coefficients) == len(total_members_available) == len(max_events) == len(min_members) == len(min_events), "Array length mismatch"
    
    # 2. VARIABLES
    x = {i: model.addVar(vtype=GRB.INTEGER, name=f"x_{i}", lb=min_members[i], ub=total_members_available[i]) for i in regions}
    y = {i: model.addVar(vtype=GRB.INTEGER, name=f"y_{i}", lb=min_events[i], ub=max_events[i]) for i in regions}
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(gp.quicksum(influence_coefficients[i] * x[i] for i in regions), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    # Budget Constraint
    model.addConstr(gp.quicksum(cost_per_event * y[i] for i in regions) <= total_budget, name="budget_constraint")
    
    # Member Assignment Upper Bound
    for i in regions:
        model.addConstr(x[i] <= total_members_available[i], name=f"member_upper_bound_{i}")
    
    # Member Assignment Lower Bound
    for i in regions:
        model.addConstr(x[i] >= min_members[i], name=f"member_lower_bound_{i}")
    
    # Event Organization Upper Bound
    for i in regions:
        model.addConstr(y[i] <= max_events[i], name=f"event_upper_bound_{i}")
    
    # Event Organization Lower Bound
    for i in regions:
        model.addConstr(y[i] >= min_events[i], name=f"event_lower_bound_{i}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in regions:
            print(f"Region {i}: Members assigned = {x[i].x}, Events organized = {y[i].x}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization
optimize_party_people()
```

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

## 6. DOCplex Implementation

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

from docplex.mp.model import Model

def party_people_optimization():
    """Optimize the allocation of party members to events across regions."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="party_people")
    
    # Data from the problem statement
    regions = [1, 2, 3]
    influence_coefficients = {1: 0.6, 2: 0.7, 3: 0.5}
    total_members_available = {1: 20, 2: 25, 3: 15}
    max_events = {1: 5, 2: 6, 3: 4}
    min_members = {1: 5, 2: 6, 3: 4}
    min_events = {1: 1, 2: 2, 3: 1}
    cost_per_event = 6000
    total_budget = 150000
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(regions) == len(influence_coefficients) == len(total_members_available) == len(max_events) == len(min_members) == len(min_events), "Array length mismatch"
    
    # 2. VARIABLES
    x = {i: mdl.integer_var(name=f"x_{i}", lb=0) for i in regions}  # Members assigned
    y = {i: mdl.integer_var(name=f"y_{i}", lb=0) for i in regions}  # Events organized
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum(influence_coefficients[i] * x[i] for i in regions)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Budget Constraint
    total_cost = mdl.sum(cost_per_event * y[i] for i in regions)
    mdl.add_constraint(total_cost <= total_budget, ctname="budget_constraint")
    
    # Member Assignment Upper Bound
    for i in regions:
        mdl.add_constraint(x[i] <= total_members_available[i], ctname=f"member_upper_{i}")
    
    # Member Assignment Lower Bound
    for i in regions:
        mdl.add_constraint(x[i] >= min_members[i], ctname=f"member_lower_{i}")
    
    # Event Organization Upper Bound
    for i in regions:
        mdl.add_constraint(y[i] <= max_events[i], ctname=f"event_upper_{i}")
    
    # Event Organization Lower Bound
    for i in regions:
        mdl.add_constraint(y[i] >= min_events[i], ctname=f"event_lower_{i}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in regions:
            x_value = solution.get_value(x[i])
            y_value = solution.get_value(y[i])
            print(f"Region {i}: Members assigned = {x_value}, Events organized = {y_value}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

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

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

## 7. Pyomo Implementation

```python
#!/usr/bin/env python3
"""
Pyomo 6.9.2 Implementation for Party People Optimization Problem
"""

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

def party_people_optimization():
    """Optimize member and event allocation to maximize influence across regions."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    regions = [1, 2, 3]
    influence_coefficients = {1: 0.6, 2: 0.7, 3: 0.5}
    total_members_available = {1: 20, 2: 25, 3: 15}
    max_events = {1: 5, 2: 6, 3: 4}
    min_members = {1: 5, 2: 6, 3: 4}
    min_events = {1: 1, 2: 2, 3: 1}
    cost_per_event = 6000
    total_budget = 150000
    
    # CRITICAL: Validate array lengths before indexing
    assert len(regions) == len(influence_coefficients) == len(total_members_available) == len(max_events) == len(min_members) == len(min_events), "Array length mismatch"
    
    # 3. SETS (Pyomo way to define indices)
    model.I = pyo.Set(initialize=regions)
    
    # 4. PARAMETERS (data containers)
    model.influence_coefficient = pyo.Param(model.I, initialize=influence_coefficients)
    model.total_members_available = pyo.Param(model.I, initialize=total_members_available)
    model.max_events = pyo.Param(model.I, initialize=max_events)
    model.min_members = pyo.Param(model.I, initialize=min_members)
    model.min_events = pyo.Param(model.I, initialize=min_events)
    model.cost_per_event = pyo.Param(initialize=cost_per_event)
    model.total_budget = pyo.Param(initialize=total_budget)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.I, within=pyo.NonNegativeIntegers)  # Number of members assigned
    model.y = pyo.Var(model.I, within=pyo.NonNegativeIntegers)  # Number of events organized
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.influence_coefficient[i] * model.x[i] for i in model.I)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Budget Constraint
    def budget_rule(model):
        return sum(model.cost_per_event * model.y[i] for i in model.I) <= model.total_budget
    model.budget_constraint = pyo.Constraint(rule=budget_rule)
    
    # Member Assignment Upper Bound
    def member_upper_rule(model, i):
        return model.x[i] <= model.total_members_available[i]
    model.member_upper_constraint = pyo.Constraint(model.I, rule=member_upper_rule)
    
    # Member Assignment Lower Bound
    def member_lower_rule(model, i):
        return model.x[i] >= model.min_members[i]
    model.member_lower_constraint = pyo.Constraint(model.I, rule=member_lower_rule)
    
    # Event Organization Upper Bound
    def event_upper_rule(model, i):
        return model.y[i] <= model.max_events[i]
    model.event_upper_constraint = pyo.Constraint(model.I, rule=event_upper_rule)
    
    # Event Organization Lower Bound
    def event_lower_rule(model, i):
        return model.y[i] >= model.min_events[i]
    model.event_lower_constraint = pyo.Constraint(model.I, rule=event_lower_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("Optimal solution found!")
        print(f"Optimal value: {pyo.value(model.objective)}")
        
        # Extract variable values
        print("\nVariable values:")
        for i in model.I:
            x_val = pyo.value(model.x[i])
            y_val = pyo.value(model.y[i])
            print(f"Region {i}: Members = {int(x_val)}, Events = {int(y_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

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

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 37.0
**Execution Time**: 0.90 seconds
**Reliability**: high
**Analysis**: Pyomo found the optimal solution but had the longest execution time among the solvers.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 37.00 | 0.17s | N/A | N/A |
| Docplex | OPTIMAL | 37.00 | 1.05s | N/A | N/A |
| Pyomo | OPTIMAL | 37.00 | 0.90s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 37.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is recommended due to its fast execution time and high reliability in finding the optimal solution.

### Business Interpretation
**Overall Strategy**: The optimal solution maximizes the total influence across all regions within the given budget and constraints.
**Objective Value Meaning**: The optimal objective value of 37.0 represents the maximum total influence achievable across all regions given the constraints.
**Resource Allocation Summary**: Resources should be allocated to maximize the number of members assigned to events in each region, ensuring that the budget and minimum/maximum event and member constraints are satisfied.
**Implementation Recommendations**: Implement the solution by assigning the optimal number of members to events in each region and organizing the required number of events, ensuring adherence to the budget and constraints.