## 4. Mathematical Optimization Formulation

#### Decision Variables
- Let \( x_i \) be a binary decision variable for each property \( i \), where \( x_i = 1 \) if property \( i \) is selected for sale, and \( x_i = 0 \) otherwise.

#### Objective Function
Maximize the total profit from selling selected properties:
\[ \text{Maximize } \sum_{i} (p_i - c_i) \times x_i \]

Where:
- \( p_i \) is the agreed selling price of property \( i \) (from `Properties.agreed_selling_price`).
- \( c_i \) is the vendor requested price of property \( i \) (from `Properties.vendor_requested_price`).

Using the provided data:
\[ \text{Maximize } (120000 - 110000) \times x_1 + (180000 - 170000) \times x_2 + (250000 - 240000) \times x_3 + (95000 - 90000) \times x_4 + (130000 - 125000) \times x_5 \]
\[ \text{Maximize } 10000 \times x_1 + 10000 \times x_2 + 10000 \times x_3 + 5000 \times x_4 + 5000 \times x_5 \]

#### Constraints
1. **Maximum Number of Properties to Sell:**
   \[ \sum_{i} x_i \leq \text{max\_properties\_to\_sell} \]
   From `ConstraintBounds.bound_value` for `max_properties_to_sell`:
   \[ x_1 + x_2 + x_3 + x_4 + x_5 \leq 3 \]

2. **Maximum Total Number of Rooms:**
   \[ \sum_{i} r_i \times x_i \leq \text{max\_total\_rooms} \]
   Where \( r_i \) is the room count of property \( i \) (from `Properties.room_count`).
   Using the provided data:
   \[ 3x_1 + 4x_2 + 5x_3 + 2x_4 + 3x_5 \leq 12 \]

3. **Minimum Number of Properties with Desired Feature:**
   \[ \sum_{i \in D} x_i \geq \text{min\_desired\_features} \]
   Where \( D \) is the set of properties with the desired feature (`Properties.apt_feature_1 = 'desired_feature'`).
   Using the provided data:
   \[ x_1 + x_3 + x_4 \geq 2 \]

Data Source Verification:
- Objective function coefficients \( p_i \) and \( c_i \) are from `Properties.agreed_selling_price` and `Properties.vendor_requested_price`.
- Room counts \( r_i \) are from `Properties.room_count`.
- Constraint bounds are from `ConstraintBounds.bound_value` for respective constraints.
- Desired feature properties are identified from `Properties.apt_feature_1`.

## 5. Gurobipy Implementation

```python
# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_real_estate_properties():
    """Optimize the selection of real estate properties for sale to maximize profit."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("real_estate_properties")
    
    # Data from the problem context
    agreed_selling_prices = [120000.0, 180000.0, 250000.0, 95000.0, 130000.0]
    vendor_requested_prices = [110000.0, 170000.0, 240000.0, 90000.0, 125000.0]
    room_counts = [3, 4, 5, 2, 3]
    features = ['desired_feature', 'other_feature', 'desired_feature', 'desired_feature', 'other_feature']
    
    # Constraint bounds
    max_properties_to_sell = 3
    max_total_rooms = 12
    min_desired_features = 2
    
    n_properties = len(agreed_selling_prices)
    
    # CRITICAL: Validate array lengths before loops
    assert len(agreed_selling_prices) == len(vendor_requested_prices) == len(room_counts) == len(features) == n_properties, "Array length mismatch"
    
    # 2. VARIABLES
    x = {i: model.addVar(vtype=GRB.BINARY, name=f"x_{i}") for i in range(n_properties)}
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(gp.quicksum((agreed_selling_prices[i] - vendor_requested_prices[i]) * x[i] for i in range(n_properties)), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    
    # Maximum number of properties to sell
    model.addConstr(gp.quicksum(x[i] for i in range(n_properties)) <= max_properties_to_sell, name="max_properties_to_sell")
    
    # Maximum total number of rooms
    model.addConstr(gp.quicksum(room_counts[i] * x[i] for i in range(n_properties)) <= max_total_rooms, name="max_total_rooms")
    
    # Minimum number of properties with desired feature
    desired_feature_indices = [i for i in range(n_properties) if features[i] == 'desired_feature']
    model.addConstr(gp.quicksum(x[i] for i in desired_feature_indices) >= min_desired_features, name="min_desired_features")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_properties):
            if x[i].x > 1e-6:
                print(f"Property {i+1} selected: x[{i}] = {x[i].x:.3f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_real_estate_properties()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 30000.0
**Execution Time**: 0.36 seconds
**Reliability**: high
**Analysis**: Gurobipy found an optimal solution quickly with the expected optimal value. The decision variables satisfy all constraints.

## 6. DOCplex Implementation

```python
# Complete DOCPLEX implementation

from docplex.mp.model import Model

def real_estate_optimization():
    """Optimization model for selecting real estate properties to maximize profit."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="real_estate_properties")
    
    # Data from the problem statement
    agreed_selling_prices = [120000.0, 180000.0, 250000.0, 95000.0, 130000.0]
    vendor_requested_prices = [110000.0, 170000.0, 240000.0, 90000.0, 125000.0]
    room_counts = [3, 4, 5, 2, 3]
    features = ['desired_feature', 'other_feature', 'desired_feature', 'desired_feature', 'other_feature']
    
    # Constraint bounds
    max_properties_to_sell = 3
    max_total_rooms = 12
    min_desired_features = 2
    
    # CRITICAL: Validate array lengths to prevent IndexError
    n_properties = len(agreed_selling_prices)
    assert len(vendor_requested_prices) == len(room_counts) == len(features) == n_properties, "Array length mismatch"
    safe_range = range(n_properties)  # Safe indexing
    
    # 2. VARIABLES
    x = {i: mdl.binary_var(name=f"x_{i}") for i in safe_range}
    
    # 3. OBJECTIVE FUNCTION
    profits = [agreed_selling_prices[i] - vendor_requested_prices[i] for i in safe_range]
    objective = mdl.sum(profits[i] * x[i] for i in safe_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Maximum number of properties to sell
    mdl.add_constraint(mdl.sum(x[i] for i in safe_range) <= max_properties_to_sell, ctname="max_properties")
    
    # Maximum total number of rooms
    mdl.add_constraint(mdl.sum(room_counts[i] * x[i] for i in safe_range) <= max_total_rooms, ctname="max_rooms")
    
    # Minimum number of properties with desired feature
    desired_feature_indices = [i for i in safe_range if features[i] == 'desired_feature']
    mdl.add_constraint(mdl.sum(x[i] for i in desired_feature_indices) >= min_desired_features, ctname="min_desired_features")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_range:
            value = solution.get_value(x[i])
            if value > 1e-6:
                print(f"x[{i}] = {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
real_estate_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 30000.0
**Execution Time**: 1.55 seconds
**Reliability**: high
**Analysis**: DOCplex also found an optimal solution with the same optimal value as Gurobipy, albeit with a longer execution time.

## 7. Pyomo Implementation

```python
# Complete PYOMO implementation

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

def real_estate_optimization():
    """Optimization model for maximizing profit from real estate properties"""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Property data
    agreed_selling_prices = [120000.0, 180000.0, 250000.0, 95000.0, 130000.0]
    vendor_requested_prices = [110000.0, 170000.0, 240000.0, 90000.0, 125000.0]
    room_counts = [3, 4, 5, 2, 3]
    features = ['desired_feature', 'other_feature', 'desired_feature', 'desired_feature', 'other_feature']
    
    # Constraint bounds
    max_properties_to_sell = 3
    max_total_rooms = 12
    min_desired_features = 2
    
    # CRITICAL: Validate array lengths before indexing
    n_properties = len(agreed_selling_prices)
    assert len(vendor_requested_prices) == len(room_counts) == len(features) == n_properties, "Array length mismatch"
    
    # 3. SETS
    model.I = pyo.RangeSet(1, n_properties)  # 1-based indexing
    
    # 4. PARAMETERS
    model.agreed_price = pyo.Param(model.I, initialize={i+1: agreed_selling_prices[i] for i in range(n_properties)})
    model.vendor_price = pyo.Param(model.I, initialize={i+1: vendor_requested_prices[i] for i in range(n_properties)})
    model.room_count = pyo.Param(model.I, initialize={i+1: room_counts[i] for i in range(n_properties)})
    model.feature = pyo.Param(model.I, initialize={i+1: features[i] for i in range(n_properties)})
    
    # 5. VARIABLES
    model.x = pyo.Var(model.I, within=pyo.Binary)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum((model.agreed_price[i] - model.vendor_price[i]) * model.x[i] for i in model.I)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.maximize)
    
    # 7. CONSTRAINTS
    
    # Maximum number of properties to sell
    def max_properties_rule(model):
        return sum(model.x[i] for i in model.I) <= max_properties_to_sell
    model.max_properties_constraint = pyo.Constraint(rule=max_properties_rule)
    
    # Maximum total number of rooms
    def max_rooms_rule(model):
        return sum(model.room_count[i] * model.x[i] for i in model.I) <= max_total_rooms
    model.max_rooms_constraint = pyo.Constraint(rule=max_rooms_rule)
    
    # Minimum number of properties with desired feature
    def min_desired_features_rule(model):
        return sum(model.x[i] for i in model.I if model.feature[i] == 'desired_feature') >= min_desired_features
    model.min_desired_features_constraint = pyo.Constraint(rule=min_desired_features_rule)
    
    # 8. SOLVING WITH GUROBI
    solver = SolverFactory('gurobi')
    
    # Solve the model
    results = solver.solve(model, tee=True)
    
    # 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("\nSelected properties:")
        for i in model.I:
            x_val = pyo.value(model.x[i])
            if x_val > 0.5:  # Only print selected properties
                print(f"Property {i} selected")
        
    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
real_estate_optimization()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 30000.0
**Execution Time**: 2.11 seconds
**Reliability**: high
**Analysis**: Pyomo achieved the optimal solution consistent with the other solvers, though it took the longest time to execute.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 30000.00 | 0.36s | N/A | N/A |
| Docplex | OPTIMAL | 30000.00 | 1.55s | N/A | N/A |
| Pyomo | OPTIMAL | 30000.00 | 2.11s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 30000.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy provided the optimal solution with the shortest execution time, indicating efficiency and reliability.

### Optimal Decision Variables
- **x_1** = 1.000
  - *Business Meaning*: Sell property 2, which contributes to the profit maximization.
- **x_2** = 1.000
  - *Business Meaning*: Sell property 3, which contributes to the profit maximization.
- **x_3** = 1.000
  - *Business Meaning*: Resource allocation for x_3
- **x_4** = 0.000
  - *Business Meaning*: Resource allocation for x_4
- **x_5** = 0.000
  - *Business Meaning*: Resource allocation for x_5

### Business Interpretation
**Overall Strategy**: The optimal solution suggests selling properties 1, 2, and 3 to maximize profit while adhering to constraints.
**Objective Value Meaning**: The optimal objective value of 30000 represents the maximum profit achievable under the given constraints.
**Resource Allocation Summary**: Allocate resources to facilitate the sale of properties 1, 2, and 3, ensuring compliance with room and feature constraints.
**Implementation Recommendations**: Proceed with the sale of the selected properties, ensuring all legal and logistical aspects are managed efficiently.