## 4. Mathematical Optimization Formulation

#### Decision Variables
Let \( x_{ij} \) be the amount of space allocated to company \( i \) in building \( j \), where:
- \( i \in \{1, 2, 3\} \) (company IDs)
- \( j \in \{1, 2, 3\} \) (building IDs)

#### Objective Function
Minimize the total leasing cost:
\[
\text{Minimize } Z = 55x_{11} + 55x_{21} + 55x_{31} + 65x_{12} + 65x_{22} + 65x_{32} + 45x_{13} + 45x_{23} + 45x_{33}
\]

#### Constraints
1. **Minimum Space Requirement**:
   \[
   x_{11} + x_{12} + x_{13} \geq 1200 \quad \text{(Company 1)}
   \]
   \[
   x_{21} + x_{22} + x_{23} \geq 1800 \quad \text{(Company 2)}
   \]
   \[
   x_{31} + x_{32} + x_{33} \geq 900 \quad \text{(Company 3)}
   \]

2. **Building Capacity**:
   \[
   x_{11} + x_{21} + x_{31} \leq 6000 \quad \text{(Building 1)}
   \]
   \[
   x_{12} + x_{22} + x_{32} \leq 7000 \quad \text{(Building 2)}
   \]
   \[
   x_{13} + x_{23} + x_{33} \leq 5000 \quad \text{(Building 3)}
   \]

3. **Non-Negative Allocation**:
   \[
   x_{ij} \geq 0 \quad \forall i, j
   \]

#### Data Source Verification
- **Objective Function Coefficients**: `cost_per_sqft.cost_per_sqft`
  - Building 1: 55.0
  - Building 2: 65.0
  - Building 3: 45.0
- **Minimum Space Requirement Constants**: `required_space.required_space`
  - Company 1: 1200
  - Company 2: 1800
  - Company 3: 900
- **Building Capacity Constants**: `available_space.available_space`
  - Building 1: 6000
  - Building 2: 7000
  - Building 3: 5000

This formulation provides a complete, immediately solvable LINEAR mathematical model with all numerical coefficients derived from the provided data.

## 5. Gurobipy Implementation

```python
#!/usr/bin/env python3
"""
Gurobipy 12.0.2 Implementation for Office Space Allocation Optimization
"""

import gurobipy as gp
from gurobipy import GRB

def optimize_office_allocation():
    """Optimize the allocation of office spaces across multiple buildings to minimize total leasing cost."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("office_allocation")
    
    # Data from the problem
    buildings = [1, 2, 3]
    companies = [1, 2, 3]
    
    cost_per_sqft = {
        1: 55.0,
        2: 65.0,
        3: 45.0
    }
    
    required_space = {
        1: 1200,
        2: 1800,
        3: 900
    }
    
    available_space = {
        1: 6000,
        2: 7000,
        3: 5000
    }
    
    # CRITICAL: Validate array lengths before loops
    assert len(buildings) == len(cost_per_sqft) == len(available_space), "Building data length mismatch"
    assert len(companies) == len(required_space), "Company data length mismatch"
    
    # 2. VARIABLES
    # Decision variables: x_ij = space allocated to company i in building j
    x = model.addVars(companies, buildings, vtype=GRB.CONTINUOUS, name="x", lb=0)
    
    # 3. OBJECTIVE FUNCTION
    # Minimize total leasing cost
    model.setObjective(
        gp.quicksum(cost_per_sqft[j] * x[i, j] for i in companies for j in buildings),
        GRB.MINIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # Minimum Space Requirement for each company
    for i in companies:
        model.addConstr(
            gp.quicksum(x[i, j] for j in buildings) >= required_space[i],
            name=f"min_space_company_{i}"
        )
    
    # Building Capacity for each building
    for j in buildings:
        model.addConstr(
            gp.quicksum(x[i, j] for i in companies) <= available_space[j],
            name=f"capacity_building_{j}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in companies:
            for j in buildings:
                if x[i, j].x > 1e-6:
                    print(f"Space allocated to company {i} in building {j}: {x[i, j].x:.3f}")
    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__":
    optimize_office_allocation()
```

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

## 6. DOCplex Implementation

```python
#!/usr/bin/env python3
"""
DOCplex 2.29.245 Implementation for Office Space Allocation Optimization
"""

from docplex.mp.model import Model

def optimize_office_allocation():
    """Optimize the allocation of office spaces across multiple buildings to minimize total leasing cost."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="office_space_allocation")
    
    # Data from the problem
    buildings = [1, 2, 3]
    companies = [1, 2, 3]
    
    cost_per_sqft = {
        1: 55.0,
        2: 65.0,
        3: 45.0
    }
    
    required_space = {
        1: 1200,
        2: 1800,
        3: 900
    }
    
    available_space = {
        1: 6000,
        2: 7000,
        3: 5000
    }
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(buildings) == len(cost_per_sqft) == len(available_space), "Building data length mismatch"
    assert len(companies) == len(required_space), "Company data length mismatch"
    
    # 2. VARIABLES
    # Decision variables: x_{ij} = space allocated to company i in building j
    x = {(i, j): mdl.continuous_var(name=f"x_{i}_{j}", lb=0) for i in companies for j in buildings}
    
    # 3. OBJECTIVE FUNCTION
    # Minimize total leasing cost
    objective = mdl.sum(cost_per_sqft[j] * x[(i, j)] for i in companies for j in buildings)
    mdl.minimize(objective)
    
    # 4. CONSTRAINTS
    
    # Minimum Space Requirement for each company
    for i in companies:
        total_space = mdl.sum(x[(i, j)] for j in buildings)
        mdl.add_constraint(total_space >= required_space[i], ctname=f"min_space_{i}")
    
    # Building Capacity for each building
    for j in buildings:
        total_allocated = mdl.sum(x[(i, j)] for i in companies)
        mdl.add_constraint(total_allocated <= available_space[j], ctname=f"capacity_{j}")
    
    # Non-Negative Allocation is already handled by lb=0 in variable definition
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in companies:
            for j in buildings:
                value = solution.get_value(x[(i, j)])
                if value > 1e-6:
                    print(f"Space allocated to Company {i} in Building {j}: {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

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

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

## 7. Pyomo Implementation

```python
#!/usr/bin/env python3
"""
Pyomo 6.9.2 Implementation for Office Space Allocation Optimization
"""

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

def optimize_office_allocation():
    """Optimize the allocation of office spaces across multiple buildings to minimize total leasing cost."""
    
    # 1. MODEL CREATION
    model = pyo.ConcreteModel()
    
    # 2. DATA SETUP
    # Define sets for companies and buildings
    companies = [1, 2, 3]
    buildings = [1, 2, 3]
    
    # Cost per square foot for each building
    cost_per_sqft = {
        1: 55.0,
        2: 65.0,
        3: 45.0
    }
    
    # Minimum space required by each company
    required_space = {
        1: 1200,
        2: 1800,
        3: 900
    }
    
    # Total available space in each building
    available_space = {
        1: 6000,
        2: 7000,
        3: 5000
    }
    
    # CRITICAL: Validate array lengths before indexing
    assert len(cost_per_sqft) == len(buildings), "Cost per sqft data length mismatch"
    assert len(required_space) == len(companies), "Required space data length mismatch"
    assert len(available_space) == len(buildings), "Available space data length mismatch"
    
    # 3. SETS
    model.companies = pyo.Set(initialize=companies)
    model.buildings = pyo.Set(initialize=buildings)
    
    # 4. PARAMETERS
    model.cost_per_sqft = pyo.Param(model.buildings, initialize=cost_per_sqft)
    model.required_space = pyo.Param(model.companies, initialize=required_space)
    model.available_space = pyo.Param(model.buildings, initialize=available_space)
    
    # 5. VARIABLES
    model.space_allocated = pyo.Var(model.companies, model.buildings, within=pyo.NonNegativeReals)
    
    # 6. OBJECTIVE FUNCTION
    def obj_rule(model):
        return sum(model.cost_per_sqft[j] * model.space_allocated[i, j] for i in model.companies for j in model.buildings)
    model.objective = pyo.Objective(rule=obj_rule, sense=pyo.minimize)
    
    # 7. CONSTRAINTS
    
    # Minimum Space Requirement
    def min_space_rule(model, i):
        return sum(model.space_allocated[i, j] for j in model.buildings) >= model.required_space[i]
    model.min_space_constraint = pyo.Constraint(model.companies, rule=min_space_rule)
    
    # Building Capacity
    def building_capacity_rule(model, j):
        return sum(model.space_allocated[i, j] for i in model.companies) <= model.available_space[j]
    model.building_capacity_constraint = pyo.Constraint(model.buildings, rule=building_capacity_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(f"Optimal value: {pyo.value(model.objective)}")
        
        # Extract variable values
        print("\nSpace Allocated:")
        for i in model.companies:
            for j in model.buildings:
                allocated = pyo.value(model.space_allocated[i, j])
                if allocated > 1e-6:  # Only print non-zero values
                    print(f"Company {i} in Building {j}: {allocated:.2f} sqft")
        
    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__":
    optimize_office_allocation()
```

### Execution Results
**Status**: OPTIMAL
**Optimal Value**: 175500.0
**Execution Time**: 0.93 seconds
**Reliability**: high
**Analysis**: Pyomo found the optimal solution with an execution time between Gurobipy and DOCplex.

## 8. Cross-Solver Analysis and Final Recommendation

### Solver Results Comparison

| Solver | Status | Optimal Value | Execution Time | Decision Variables | Retry Attempt |
|--------|--------|---------------|----------------|-------------------|---------------|
| Gurobipy | OPTIMAL | 175500.00 | 0.19s | N/A | N/A |
| Docplex | OPTIMAL | 175500.00 | 1.16s | N/A | N/A |
| Pyomo | OPTIMAL | 175500.00 | 0.93s | N/A | N/A |

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

### Final Recommendation
**Recommended Optimal Value**: 175500.0
**Confidence Level**: HIGH
**Preferred Solver(s)**: gurobipy
**Reasoning**: Gurobipy is recommended due to its faster execution time while still providing the same optimal solution as the other solvers.

### Business Interpretation
**Overall Strategy**: The optimal total leasing cost is $175,500, which is the minimum cost achievable given the constraints.
**Objective Value Meaning**: The optimal total leasing cost of $175,500 ensures that all companies' space requirements are met while minimizing the cost.
**Resource Allocation Summary**: Allocate space to companies in buildings such that the total cost is minimized while meeting all constraints.
**Implementation Recommendations**: Implement the space allocation as per the optimal solution. Ensure that the allocations do not exceed building capacities and meet the minimum space requirements for each company.