providers = ['AWS', 'Azure', 'Google']
services = ['C1', 'C2', 'AI/ML']
m = len(providers) # Number of providers
n = len(services) # Number of services
Decision variables
x = model.addVars(m, n, vtype=GRB.CONTINUOUS, name="x") # Allocation variables
w = model.addVars(m, n, m, vtype=GRB.CONTINUOUS, name="w") # Egress transfer flow
y = model.addVars(m, n, vtype=GRB.BINARY, name="y") # Provider selection binary variables
Objective function: Minimize the total cost (service allocation + egress costs)
Supply-Demand Constraint: Ensure total allocation for each service meets the demand
model.addConstrs(
quicksum(x[i, j] for i in range(m)) == demand[services[j]]
for j in range(n)
)
Supply Constraint: Ensure allocation for each service from each provider doesn't exceed its supply for that service
model.addConstrs(
x[i, j] <= [supply_AWS, supply_Azure, supply_Google][i][services[j]]
for i in range(m)
for j in range(n)
)
One Provider Per Service Constraint: Ensure only one provider per service
model.addConstrs(
quicksum(y[i, j] for i in range(m)) == 1
for j in range(n)
)
Egress Transfer Constraints: Ensure egress flows match allocations
for i in range(m):
for j in range(n):
for k in range(m):
if i != k: # Only for different providers, flow is counted in term of hours.
model.addConstr(w[i, j, k] == x[i, j], name=f"egress_transfer_{i}_{j}_{k}")
Budget Constraint: Ensure the total cost (service allocation + egress costs) does not exceed the budget
from gurobipy import GRB, Model, quicksum
Example data for costs, supply, and demand
costs_AWS = {'C1': 0.1664, 'C2': 0.08, 'AI/ML': 3.06}
costs_Azure = {'C1': 0.2021, 'C2': 0.10, 'AI/ML': 0.90}
costs_Google = {'C1': 0.1900, 'C2': 0.10, 'AI/ML': 1.80}
supply_AWS = {'C1': 100, 'C2': 300, 'AI/ML': 50}
supply_Azure = {'C1': 120, 'C2': 180, 'AI/ML': 80}
supply_Google = {'C1': 130, 'C2': 200, 'AI/ML': 70}
demand = {'C1': 100, 'C2': 50, 'AI/ML': 50}
Egress cost between different providers (no self transfer cost)
egress_cost = {
('AWS', 'Azure'): 0.10, # hourly rate
('AWS', 'Google'): 0.15,
('Azure', 'AWS'): 0.10,
('Azure', 'Google'): 0.12,
('Google', 'AWS'): 0.15,
('Google', 'Azure'): 0.12
}
Budget constraint
budget = 1000
Create a model
model = Model()
Define decision variables
providers = ['AWS', 'Azure', 'Google']
services = ['C1', 'C2', 'AI/ML']
m = len(providers) # Number of providers
n = len(services) # Number of services
Decision variables
x = model.addVars(m, n, vtype=GRB.CONTINUOUS, name="x") # Allocation variables
w = model.addVars(m, n, m, vtype=GRB.CONTINUOUS, name="w") # Egress transfer flow
y = model.addVars(m, n, vtype=GRB.BINARY, name="y") # Provider selection binary variables
Objective function: Minimize the total cost (service allocation + egress costs)
model.setObjective(
quicksum(
costs_AWS[services[j]] * x[0, j] +
costs_Azure[services[j]] * x[1, j] +
costs_Google[services[j]] * x[2, j]
for j in range(n)
) +
quicksum(
egress_cost.get(('AWS', 'Azure'), 0) * w[0, j, 1] +
egress_cost.get(('AWS', 'Google'), 0) * w[0, j, 2] +
egress_cost.get(('Azure', 'AWS'), 0) * w[1, j, 0] +
egress_cost.get(('Azure', 'Google'), 0) * w[1, j, 2] +
egress_cost.get(('Google', 'AWS'), 0) * w[2, j, 0] +
egress_cost.get(('Google', 'Azure'), 0) * w[2, j, 1]
for j in range(n)
),
GRB.MINIMIZE
)
Supply-Demand Constraint: Ensure total allocation for each service meets the demand
model.addConstrs(
quicksum(x[i, j] for i in range(m)) == demand[services[j]]
for j in range(n)
)
Supply Constraint: Ensure allocation for each service from each provider doesn't exceed its supply for that service
model.addConstrs(
x[i, j] <= [supply_AWS, supply_Azure, supply_Google][i][services[j]]
for i in range(m)
for j in range(n)
)
One Provider Per Service Constraint: Ensure only one provider per service
model.addConstrs(
quicksum(y[i, j] for i in range(m)) == 1
for j in range(n)
)
Egress Transfer Constraints: Ensure egress flows match allocations
for i in range(m):
for j in range(n):
for k in range(m):
if i != k: # Only for different providers, flow is counted in term of hours.
model.addConstr(w[i, j, k] == x[i, j], name=f"egress_transfer_{i}_{j}_{k}")
Budget Constraint: Ensure the total cost (service allocation + egress costs) does not exceed the budget
total_cost = quicksum(
costs_AWS[services[j]] * x[0, j] +
costs_Azure[services[j]] * x[1, j] +
costs_Google[services[j]] * x[2, j]
for j in range(n)
) + quicksum(
egress_cost.get(('AWS', 'Azure'), 0) * w[0, j, 1] +
egress_cost.get(('AWS', 'Google'), 0) * w[0, j, 2] +
egress_cost.get(('Azure', 'AWS'), 0) * w[1, j, 0] +
egress_cost.get(('Azure', 'Google'), 0) * w[1, j, 2] +
egress_cost.get(('Google', 'AWS'), 0) * w[2, j, 0] +
egress_cost.get(('Google', 'Azure'), 0) * w[2, j, 1]
for j in range(n)
)
model.addConstr(total_cost <= budget, "BudgetConstraint")
Solve the model
model.optimize()
Display results
if model.status == GRB.OPTIMAL:
print("Optimal solution found!")
for i in range(m):
for j in range(n):
print(f"Provider {providers[i]} service {services[j]} allocation: {x[i, j].x}")
# Output total egress cost
total_egress_cost = sum(
egress_cost.get(('AWS', 'Azure'), 0) * w[0, j, 1].x +
egress_cost.get(('AWS', 'Google'), 0) * w[0, j, 2].x +
egress_cost.get(('Azure', 'AWS'), 0) * w[1, j, 0].x +
egress_cost.get(('Azure', 'Google'), 0) * w[1, j, 2].x +
egress_cost.get(('Google', 'AWS'), 0) * w[2, j, 0].x +
egress_cost.get(('Google', 'Azure'), 0) * w[2, j, 1].x
for j in range(n)
)
print(f"Total egress cost: {total_egress_cost}")
else:
print("No optimal solution found.")