# Complete Optimization Problem and Solution: candidate_poll

## 1. Problem Context and Goals

### Context  
A political campaign is focused on maximizing the overall support rate for a candidate by strategically allocating resources to different poll sources. Each poll source has a unique effectiveness in converting unsure voters to supporters, which is influenced by the amount of money invested in that source. The campaign must decide how much to invest in each poll source to achieve the highest possible support rate, while adhering to the following operational parameters:  

- **Total Budget**: The campaign has a fixed budget of $100,000 available for investment across all poll sources. This ensures that the total investment does not exceed the available funds.  
- **Minimum Investment**: Each poll source must receive at least $1,000 in investment to ensure meaningful engagement and impact on support rates.  
- **Maximum Support Rate**: For each poll source, there is a maximum achievable support rate of 80%, reflecting practical limitations in voter conversion.  

The campaign’s decision-making process is guided by two key metrics for each poll source:  
- **Initial Support Rate**: The current level of support for the candidate in each poll source before any additional investment.  
- **Conversion Rate**: The rate at which unsure voters convert to supporters per unit of investment in each poll source.  

By leveraging these metrics, the campaign aims to allocate investments in a way that maximizes the overall support rate while staying within budget and meeting minimum investment requirements.  

### Goals  
The primary goal of this optimization problem is to maximize the total support rate for the candidate across all poll sources. This is achieved by strategically determining the amount of money to invest in each poll source, considering the initial support rate and the conversion rate of unsure voters to supporters. Success is measured by the ability to increase the overall support rate while adhering to the campaign’s budget and investment constraints.  

## 2. Constraints  

The campaign’s investment strategy is subject to the following constraints:  

1. **Budget Constraint**: The total amount of money invested across all poll sources must not exceed the campaign’s total budget of $100,000.  
2. **Minimum Investment Constraint**: Each poll source must receive at least $1,000 in investment to ensure meaningful engagement.  
3. **Maximum Support Rate Constraint**: For each poll source, the combined effect of the initial support rate and the additional support gained from investment must not exceed the maximum achievable support rate of 80%.  

These constraints ensure that the campaign’s investment strategy is both financially feasible and operationally effective.  

## 3. Available Data  

### Database Schema  
```sql
-- Iteration 1 Database Schema
-- Objective: Schema changes include creating new tables for missing optimization parameters and updating business configuration logic to handle scalar parameters and formulas.

CREATE TABLE poll_source_conversion_rates (
  Poll_Source_ID INTEGER,
  Conversion_rate FLOAT
);

CREATE TABLE candidate (
  Poll_Source_ID INTEGER,
  Support_rate FLOAT
);

CREATE TABLE poll_source_investments (
  Poll_Source_ID INTEGER,
  Investment FLOAT
);
```

### Data Dictionary  
The following tables and columns are used in this optimization problem:  

- **poll_source_conversion_rates**:  
  - **Poll_Source_ID**: Unique identifier for each poll source.  
  - **Conversion_rate**: The rate at which unsure voters convert to supporters per unit of investment in the poll source. This is a key coefficient in the optimization objective.  

- **candidate**:  
  - **Poll_Source_ID**: Unique identifier for each poll source.  
  - **Support_rate**: The initial support rate for the candidate in the poll source before any additional investment. This is a key coefficient in the optimization objective.  

- **poll_source_investments**:  
  - **Poll_Source_ID**: Unique identifier for each poll source.  
  - **Investment**: The amount of money invested in the poll source. This is the 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 campaign budgets, typical conversion rates for political polls, and initial support rates that reflect a competitive race. Investments were set to ensure they are within the budget and meet minimum investment requirements.

-- Realistic data for poll_source_conversion_rates
INSERT INTO poll_source_conversion_rates (Poll_Source_ID, Conversion_rate) VALUES (1, 0.05);
INSERT INTO poll_source_conversion_rates (Poll_Source_ID, Conversion_rate) VALUES (2, 0.07);
INSERT INTO poll_source_conversion_rates (Poll_Source_ID, Conversion_rate) VALUES (3, 0.1);

-- Realistic data for candidate
INSERT INTO candidate (Poll_Source_ID, Support_rate) VALUES (1, 0.3);
INSERT INTO candidate (Poll_Source_ID, Support_rate) VALUES (2, 0.4);
INSERT INTO candidate (Poll_Source_ID, Support_rate) VALUES (3, 0.5);

-- Realistic data for poll_source_investments
INSERT INTO poll_source_investments (Poll_Source_ID, Investment) VALUES (1, 1000);
INSERT INTO poll_source_investments (Poll_Source_ID, Investment) VALUES (2, 2000);
INSERT INTO poll_source_investments (Poll_Source_ID, Investment) VALUES (3, 3000);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_i \) represent the amount of money invested in poll source \( i \), where \( i \in \{1, 2, 3\} \).  
  \( x_i \) is a continuous variable (LP).

#### Objective Function
Maximize the total support rate across all poll sources:  
\[
\text{Maximize } Z = \sum_{i=1}^{3} \left( \text{Support_rate}_i + \text{Conversion_rate}_i \times x_i \right)
\]  
Where:  
- \( \text{Support_rate}_i \) is the initial support rate for poll source \( i \).  
- \( \text{Conversion_rate}_i \) is the conversion rate for poll source \( i \).

#### Constraints
1. **Budget Constraint**: The total investment must not exceed the campaign’s budget of $100,000.  
\[
\sum_{i=1}^{3} x_i \leq 100000
\]

2. **Minimum Investment Constraint**: Each poll source must receive at least $1,000 in investment.  
\[
x_i \geq 1000 \quad \forall i \in \{1, 2, 3\}
\]

3. **Maximum Support Rate Constraint**: The combined effect of the initial support rate and the additional support gained from investment must not exceed 80% for each poll source.  
\[
\text{Support_rate}_i + \text{Conversion_rate}_i \times x_i \leq 0.8 \quad \forall i \in \{1, 2, 3\}
\]

#### Data Source Verification
- \( \text{Support_rate}_i \): From `candidate.Support_rate`.  
- \( \text{Conversion_rate}_i \): From `poll_source_conversion_rates.Conversion_rate`.  
- Budget: Business configuration parameter ($100,000).  
- Minimum investment: Business configuration parameter ($1,000).  
- Maximum support rate: Business configuration parameter (80%).

#### Numerical Coefficients from Data
- \( \text{Support_rate}_1 = 0.3 \), \( \text{Support_rate}_2 = 0.4 \), \( \text{Support_rate}_3 = 0.5 \).  
- \( \text{Conversion_rate}_1 = 0.05 \), \( \text{Conversion_rate}_2 = 0.07 \), \( \text{Conversion_rate}_3 = 0.1 \).  

#### Complete Linear Model
\[
\text{Maximize } Z = 0.3 + 0.05x_1 + 0.4 + 0.07x_2 + 0.5 + 0.1x_3
\]  
Subject to:  
\[
x_1 + x_2 + x_3 \leq 100000
\]  
\[
x_1 \geq 1000, \quad x_2 \geq 1000, \quad x_3 \geq 1000
\]  
\[
0.3 + 0.05x_1 \leq 0.8, \quad 0.4 + 0.07x_2 \leq 0.8, \quad 0.5 + 0.1x_3 \leq 0.8
\]

This is a complete, immediately solvable linear programming (LP) model.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def optimize_candidate_poll():
    """Optimize the candidate poll investment strategy using Gurobipy."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("candidate_poll_optimization")
    
    # Data from the problem
    poll_sources = [1, 2, 3]
    support_rates = {1: 0.3, 2: 0.4, 3: 0.5}
    conversion_rates = {1: 0.05, 2: 0.07, 3: 0.1}
    total_budget = 100000
    min_investment = 1000
    max_support_rate = 0.8
    
    # CRITICAL: Validate array lengths before loops
    assert len(poll_sources) == len(support_rates) == len(conversion_rates), "Array length mismatch"
    
    # 2. VARIABLES
    x = {i: model.addVar(vtype=GRB.CONTINUOUS, name=f"x_{i}", lb=min_investment) for i in poll_sources}
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(support_rates[i] + conversion_rates[i] * x[i] for i in poll_sources),
        GRB.MAXIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # Budget Constraint
    model.addConstr(
        gp.quicksum(x[i] for i in poll_sources) <= total_budget,
        name="budget_constraint"
    )
    
    # Maximum Support Rate Constraint
    for i in poll_sources:
        model.addConstr(
            support_rates[i] + conversion_rates[i] * x[i] <= max_support_rate,
            name=f"max_support_rate_{i}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in poll_sources:
            print(f"Investment in poll source {i}: {x[i].x:.2f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization
optimize_candidate_poll()
```

### Execution Results
**Status**: INFEASIBLE
**Error**: Problem is infeasible
**Analysis**: Gurobipy identified the problem as infeasible, indicating that the constraints cannot be satisfied simultaneously. This is a reliable result given Gurobipy's robust algorithms.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation - Retry Attempt 4

from docplex.mp.model import Model

def optimize_candidate_poll():
    """Optimize the allocation of resources to maximize the overall support rate for a candidate."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="candidate_poll_optimization")
    
    # Data from the database
    poll_source_ids = [1, 2, 3]
    support_rates = [0.3, 0.4, 0.5]
    conversion_rates = [0.05, 0.07, 0.1]
    
    # Business configuration parameters
    total_budget = 100000
    min_investment = 1000
    max_support_rate = 0.8
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(poll_source_ids) == len(support_rates) == len(conversion_rates), "Array length mismatch"
    safe_range = range(min(len(poll_source_ids), len(support_rates), len(conversion_rates)))  # Safe indexing
    
    # 2. VARIABLES
    # Decision variables: amount of money invested in each poll source
    investments = {i: mdl.continuous_var(name=f"x_{i}", lb=min_investment) for i in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total support rate across all poll sources
    objective = mdl.sum(support_rates[i] + conversion_rates[i] * investments[i] for i in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Budget Constraint: Total investment must not exceed the campaign’s budget
    total_investment = mdl.sum(investments[i] for i in safe_range)
    mdl.add_constraint(total_investment <= total_budget, ctname="budget_constraint")
    
    # Maximum Support Rate Constraint: Combined effect must not exceed 80% for each poll source
    for i in safe_range:
        mdl.add_constraint(support_rates[i] + conversion_rates[i] * investments[i] <= max_support_rate, ctname=f"max_support_rate_{i}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_range:
            value = solution.get_value(investments[i])
            print(f"Investment in poll source {poll_source_ids[i]}: ${value:.2f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
        if mdl.solve_details.status == "infeasible":
            print("The problem is infeasible. Please check the constraints.")
        elif mdl.solve_details.status == "unbounded":
            print("The problem is unbounded. Please check the objective function.")
    
    return mdl

# Execute the optimization
optimize_candidate_poll()
```

### Execution Results
**Status**: INFEASIBLE
**Error**: Problem is infeasible
**Analysis**: DOCplex also concluded that the problem is infeasible, aligning with Gurobipy's result. DOCplex is known for its accuracy in handling LP problems.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation - Retry Attempt 4

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

def candidate_poll_optimization():
    """Optimize the allocation of resources to maximize the overall support rate for a candidate."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    poll_sources = [1, 2, 3]
    support_rates = {1: 0.3, 2: 0.4, 3: 0.5}
    conversion_rates = {1: 0.05, 2: 0.07, 3: 0.1}
    total_budget = 100000
    min_investment = 1000
    max_support_rate = 0.8
    
    # CRITICAL: Validate array lengths before indexing
    assert len(support_rates) == len(conversion_rates) == len(poll_sources), "Array length mismatch"
    
    # 3. SETS (Pyomo way to define indices)
    model.I = pyo.Set(initialize=poll_sources)
    
    # 4. PARAMETERS (data containers)
    model.support_rate = pyo.Param(model.I, initialize=support_rates)
    model.conversion_rate = pyo.Param(model.I, initialize=conversion_rates)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.I, within=pyo.NonNegativeReals, bounds=(min_investment, total_budget))
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.support_rate[i] + model.conversion_rate[i] * model.x[i] for i in model.I)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Budget Constraint
    def budget_rule(model):
        return sum(model.x[i] for i in model.I) <= total_budget
    model.budget_constraint = pyo.Constraint(rule=budget_rule)
    
    # Maximum Support Rate Constraint
    def max_support_rate_rule(model, i):
        return model.support_rate[i] + model.conversion_rate[i] * model.x[i] <= max_support_rate
    model.max_support_rate_constraint = pyo.Constraint(model.I, rule=max_support_rate_rule)
    
    # 8. SOLVING WITH GUROBI (your available solver)
    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("\nInvestment values:")
        for i in model.I:
            x_val = pyo.value(model.x[i])
            print(f"x[{i}] = {x_val:.3f}")
        
    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__":
    candidate_poll_optimization()
```

### Execution Results
**Status**: INFEASIBLE
**Error**: Problem is infeasible
**Analysis**: Pyomo returned an infeasible status, consistent with the other solvers. While Pyomo is reliable, it is slightly less efficient compared to Gurobipy and DOCplex.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | INFEASIBLE | N/A | 0.19s | N/A | 4 |
| Docplex | INFEASIBLE | N/A | 1.27s | N/A | 4 |
| Pyomo | INFEASIBLE | N/A | 0.95s | N/A | 4 |

### Solver Consistency Analysis
**Result**: All solvers produced consistent results ✓
**Consistent Solvers**: gurobipy, docplex, pyomo
**Solver Retry Summary**: gurobipy: 4 attempts, docplex: 4 attempts, pyomo: 4 attempts

### Final Recommendation
**Confidence Level**: HIGH
**Preferred Solver(s)**: multiple
**Reasoning**: All three solvers consistently identified the problem as infeasible, providing high confidence in the result. Multiple solvers were used to ensure reliability.

### Business Interpretation
**Overall Strategy**: The problem is infeasible, meaning the campaign cannot achieve the desired support rates within the given budget and constraints. This suggests a need to revise the constraints or increase the budget.
**Objective Value Meaning**: The objective value represents the total support rate across all poll sources. An infeasible problem indicates that the desired support rates cannot be achieved under the current constraints.
**Resource Allocation Summary**: No feasible allocation of the $100,000 budget satisfies all constraints. Consider revising the budget, minimum investment, or maximum support rate thresholds.
**Implementation Recommendations**: ['Review and relax the maximum support rate constraints if possible.', 'Increase the campaign budget to allow for higher investments in poll sources.', 'Re-evaluate the minimum investment requirements to ensure they are realistic.', 'Verify the accuracy of the support rates and conversion rates used in the model.']