# Complete Optimization Problem and Solution: Entrepreneur

## 1. Problem Context and Goals

### Context  
An investor is tasked with strategically allocating a fixed budget across a selection of entrepreneurs. The primary decision involves determining the amount of money to invest in each entrepreneur, with the goal of maximizing the expected return on investment. Each entrepreneur presents a unique opportunity, characterized by a specific expected return rate. The investor must consider the total budget available for investment, which serves as a constraint, ensuring that the sum of all investments does not exceed this budget. Additionally, each entrepreneur has a maximum amount they are willing to accept, which further constrains the investment decisions. The investor's objective is to maximize the total expected return, calculated as the sum of the products of each entrepreneur's expected return rate and the corresponding investment amount. This scenario is structured to align with a linear optimization formulation, focusing on precise decision-making within defined resource limitations.

### Goals  
The primary goal of this optimization problem is to maximize the total expected return from investments in various entrepreneurs. The metric for optimization is the total expected return, which is the sum of the expected return rates multiplied by the respective investment amounts for each entrepreneur. Success is measured by the ability to achieve the highest possible total expected return within the constraints of the available budget and the maximum amounts requested by the entrepreneurs. The optimization process is linear, focusing on maximizing the return while adhering to the defined constraints.

## 2. Constraints    

The investment strategy is subject to the following constraints:

- The total amount invested across all entrepreneurs must not exceed the total budget available for investment. This ensures that the investor operates within their financial means.
- The amount invested in each entrepreneur must not exceed the maximum amount requested by that entrepreneur. This constraint respects the individual limits set by each entrepreneur, ensuring that investments are realistic and acceptable.

These constraints are expressed 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 missing optimization data, modifying existing tables to improve mapping adequacy, and updating business configuration logic for scalar parameters and formulas.

CREATE TABLE Expected_Return_Coefficients (
  entrepreneur_id INTEGER,
  expected_return_rate FLOAT
);

CREATE TABLE Investments (
  entrepreneur_id INTEGER,
  amount FLOAT
);

CREATE TABLE Entrepreneur (
  entrepreneur_id INTEGER,
  Money_Requested FLOAT,
  expected_return_coefficient FLOAT
);
```

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

- **Expected_Return_Coefficients**: This table stores the expected return rates for each entrepreneur. The expected return rate serves as a coefficient in the objective function, linking the potential return to specific entrepreneurs.

  - **entrepreneur_id**: A unique identifier for each entrepreneur, used to associate return rates with specific individuals.
  - **expected_return_rate**: Represents the expected return rate for each entrepreneur, serving as a key factor in calculating the total expected return.

- **Investments**: This table tracks the amount of money invested in each entrepreneur. The investment amount is a decision variable in the optimization model.

  - **entrepreneur_id**: A unique identifier for each entrepreneur, linking investments to specific individuals.
  - **amount**: The amount of money invested in each entrepreneur, representing the decision variable in the optimization process.

- **Entrepreneur**: This table contains information about each entrepreneur, including the maximum amount they are willing to accept and their expected return rate.

  - **entrepreneur_id**: A unique identifier for each entrepreneur, serving as the primary key.
  - **Money_Requested**: The maximum amount requested by each entrepreneur, used as a constraint bound in the optimization model.
  - **expected_return_coefficient**: The expected return rate for each entrepreneur, used as an objective coefficient in the optimization process.

### Current Stored Values  
```sql
-- Iteration 1 Realistic Data
-- Generated by triple expert (business + data + optimization)
-- Values were determined based on typical investment scenarios where entrepreneurs request varying amounts and offer different expected returns. The total budget was set to allow for partial funding of all requests, ensuring a meaningful allocation decision.

-- Realistic data for Expected_Return_Coefficients
INSERT INTO Expected_Return_Coefficients (entrepreneur_id, expected_return_rate) VALUES (1, 0.12);
INSERT INTO Expected_Return_Coefficients (entrepreneur_id, expected_return_rate) VALUES (2, 0.18);
INSERT INTO Expected_Return_Coefficients (entrepreneur_id, expected_return_rate) VALUES (3, 0.22);

-- Realistic data for Investments
INSERT INTO Investments (entrepreneur_id, amount) VALUES (1, 15000);
INSERT INTO Investments (entrepreneur_id, amount) VALUES (2, 20000);
INSERT INTO Investments (entrepreneur_id, amount) VALUES (3, 25000);

-- Realistic data for Entrepreneur
INSERT INTO Entrepreneur (entrepreneur_id, Money_Requested, expected_return_coefficient) VALUES (1, 20000, 0.12);
INSERT INTO Entrepreneur (entrepreneur_id, Money_Requested, expected_return_coefficient) VALUES (2, 25000, 0.18);
INSERT INTO Entrepreneur (entrepreneur_id, Money_Requested, expected_return_coefficient) VALUES (3, 30000, 0.22);
```

## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_1 \) be the amount of money invested in Entrepreneur 1.
- Let \( x_2 \) be the amount of money invested in Entrepreneur 2.
- Let \( x_3 \) be the amount of money invested in Entrepreneur 3.

#### Objective Function
Maximize the total expected return:
\[ \text{Maximize } Z = 0.12x_1 + 0.18x_2 + 0.22x_3 \]

Data Source Verification:
- Coefficients 0.12, 0.18, and 0.22 are from `Expected_Return_Coefficients.expected_return_rate`.

#### Constraints
1. Total budget constraint:
   \[ x_1 + x_2 + x_3 \leq 60000 \]

2. Individual investment constraints:
   \[ x_1 \leq 20000 \]
   \[ x_2 \leq 25000 \]
   \[ x_3 \leq 30000 \]

3. Non-negativity constraints:
   \[ x_1 \geq 0 \]
   \[ x_2 \geq 0 \]
   \[ x_3 \geq 0 \]

Data Source Verification:
- The total budget of 60000 is derived from the sum of the realistic data for `Investments.amount`.
- Constraints \( x_1 \leq 20000 \), \( x_2 \leq 25000 \), and \( x_3 \leq 30000 \) are from `Entrepreneur.Money_Requested`.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_investments():
    """Optimize investment allocation to maximize expected return."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("investment_optimization")
    
    # Data: Expected return rates and maximum money requested by each entrepreneur
    expected_return_rates = [0.12, 0.18, 0.22]
    money_requested = [20000, 25000, 30000]
    total_budget = 60000
    
    # Number of entrepreneurs
    n_entrepreneurs = len(expected_return_rates)
    
    # CRITICAL: Validate array lengths before loops
    assert len(expected_return_rates) == len(money_requested) == n_entrepreneurs, "Array length mismatch"
    
    # 2. VARIABLES
    # Investment amounts for each entrepreneur
    investments = model.addVars(n_entrepreneurs, vtype=GRB.CONTINUOUS, name="investment", lb=0)
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total expected return
    model.setObjective(gp.quicksum(expected_return_rates[i] * investments[i] for i in range(n_entrepreneurs)), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Total budget constraint
    model.addConstr(gp.quicksum(investments[i] for i in range(n_entrepreneurs)) <= total_budget, name="budget_constraint")
    
    # Individual investment constraints
    for i in range(n_entrepreneurs):
        model.addConstr(investments[i] <= money_requested[i], name=f"max_investment_{i}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_entrepreneurs):
            if investments[i].x > 1e-6:
                print(f"Investment in entrepreneur {i+1}: {investments[i].x:.2f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_investments()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 11700.0
**Execution Time**: 0.16 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with a reliable status and consistent results.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_investments():
    """Optimize investment allocation to maximize expected return."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="investment_optimization")
    
    # Data from the problem statement
    expected_return_rates = [0.12, 0.18, 0.22]
    money_requested = [20000, 25000, 30000]
    total_budget = 60000
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(expected_return_rates) == len(money_requested), "Array length mismatch"
    safe_range = range(min(len(expected_return_rates), len(money_requested)))  # Safe indexing
    
    # 2. VARIABLES
    # Variable dictionaries for investments
    investments = {i: mdl.continuous_var(name=f"x_{i}", lb=0) for i in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total expected return
    objective = mdl.sum(expected_return_rates[i] * investments[i] for i in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS - CORRECT SYNTAX PATTERNS
    
    # Total budget constraint
    total_investment = mdl.sum(investments[i] for i in safe_range)
    mdl.add_constraint(total_investment <= total_budget, ctname="total_budget")
    
    # Individual investment constraints
    for i in safe_range:
        mdl.add_constraint(investments[i] <= money_requested[i], ctname=f"max_investment_{i}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value:.2f}")
        for i in safe_range:
            value = solution.get_value(investments[i])
            if value > 1e-6:
                print(f"Investment in entrepreneur {i+1}: {value:.2f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
optimize_investments()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 11700.0
**Execution Time**: 2.11 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution, albeit with a longer execution time, but results are consistent.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def investment_optimization():
    """Investment optimization using Pyomo"""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    entrepreneurs = [1, 2, 3]
    expected_return_rates = {1: 0.12, 2: 0.18, 3: 0.22}
    money_requested = {1: 20000, 2: 25000, 3: 30000}
    total_budget = 60000
    
    # CRITICAL: Validate data lengths before indexing
    assert len(expected_return_rates) == len(money_requested) == len(entrepreneurs), "Data length mismatch"
    
    # 3. SETS
    model.I = pyo.Set(initialize=entrepreneurs)
    
    # 4. PARAMETERS
    model.expected_return_rate = pyo.Param(model.I, initialize=expected_return_rates)
    model.money_requested = pyo.Param(model.I, initialize=money_requested)
    
    # 5. VARIABLES
    model.x = pyo.Var(model.I, within=pyo.NonNegativeReals)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.expected_return_rate[i] * model.x[i] for i in model.I)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Total budget constraint
    def budget_constraint_rule(model):
        return sum(model.x[i] for i in model.I) <= total_budget
    model.budget_constraint = pyo.Constraint(rule=budget_constraint_rule)
    
    # Individual investment constraints
    def investment_constraint_rule(model, i):
        return model.x[i] <= model.money_requested[i]
    model.investment_constraints = pyo.Constraint(model.I, rule=investment_constraint_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):.3f}")
        
        # Extract variable values
        print("\nInvestment amounts:")
        for i in model.I:
            x_val = pyo.value(model.x[i])
            if x_val > 1e-6:  # Only print non-zero values
                print(f"x[{i}] = {x_val:.2f}")
        
    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
investment_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 11700.0
**Execution Time**: 1.69 seconds
**Reliability**: high
**Analysis**: Pyomo achieved an optimal solution with consistent results, though slightly slower than Gurobipy.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 11700.00 | 0.16s | N/A | N/A |
| Docplex | OPTIMAL | 11700.00 | 2.11s | N/A | N/A |
| Pyomo | OPTIMAL | 11700.00 | 1.69s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 11700.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: multiple
**Reasoning**: All solvers provided consistent and optimal results, indicating robustness and reliability of the solution.

### Optimal Decision Variables
- **x_1** = 20000.000
  - *Business Meaning*: Investment in Entrepreneur 2, fully utilizing the maximum allowed investment of $25,000.
- **x_2** = 25000.000
  - *Business Meaning*: Investment in Entrepreneur 3, utilizing $15,000 of the $30,000 maximum allowed.
- **x_3** = 15000.000
  - *Business Meaning*: Resource allocation for x_3

### Business Interpretation
**Overall Strategy**: The optimal investment strategy maximizes expected returns within budget constraints.
**Objective Value Meaning**: The optimal objective value of $11,700 represents the maximum expected return from the investments.
**Resource Allocation Summary**: Allocate $20,000 to Entrepreneur 1, $25,000 to Entrepreneur 2, and $15,000 to Entrepreneur 3.
**Implementation Recommendations**: Proceed with the recommended investment allocations to maximize returns, ensuring compliance with budget and individual constraints.