This tutorial includes everything you need to set up decision optimization engines, build mathematical programming models, and a solve a capacitated facility location problem to do server load balancing.
When you finish this tutorial, you'll have a foundational knowledge of Prescriptive Analytics.
This notebook is part of Prescriptive Analytics for Python
It requires either an installation of CPLEX Optimizers or it can be run on IBM Watson Studio Cloud (Sign up for a free IBM Cloud account and you can start using Watson Studio Cloud right away).
Table of contents:
Prescriptive analytics (decision optimization) technology recommends actions that are based on desired outcomes. It takes into account specific scenarios, resources, and knowledge of past and current events. With this insight, your organization can make better decisions and have greater control of business outcomes.
Prescriptive analytics is the next step on the path to insight-based actions. It creates value through synergy with predictive analytics, which analyzes data to predict future outcomes.
Prescriptive analytics takes that insight to the next level by suggesting the optimal way to handle that future situation. Organizations that can act fast in dynamic conditions and make superior decisions in uncertain environments gain a strong competitive advantage.
With prescriptive analytics, you can:
In [ ]:
import sys
try:
import docplex.mp
except:
raise Exception('Please install docplex. See https://pypi.org/project/docplex/')
In [ ]:
from collections import namedtuple
In [ ]:
class TUser(namedtuple("TUser", ["id", "running", "sleeping", "current_server"])):
def __str__(self):
return self.id
In [ ]:
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
In [ ]:
try:
from urllib2 import urlopen
except ImportError:
from urllib.request import urlopen
In [ ]:
import csv
data_url = "https://github.com/vberaudi/utwt/blob/master/users.csv?raw=true"
xld = urlopen(data_url).read()
xlds = StringIO(xld.decode('utf-8'))
reader = csv.reader(xlds)
users = [(row[0], int(row[1]), int(row[2]), row[3]) for row in reader]
Given the number of teams in each division and the number of intradivisional and interdivisional games to be played, you can calculate the total number of teams and the number of weeks in the schedule, assuming every team plays exactly one game per week.
The season is split into halves, and the number of the intradivisional games that each team must play in the first half of the season is calculated.
In [ ]:
max_processes_per_server = 50
users = [TUser(*user_row) for user_row in users]
servers = list({t.current_server for t in users})
In [ ]:
from docplex.mp.environment import Environment
env = Environment()
env.print_information()
In [ ]:
from docplex.mp.model import Model
mdl = Model("truck")
In [ ]:
active_var_by_server = mdl.binary_var_dict(servers, name='isActive')
def user_server_pair_namer(u_s):
u, s = u_s
return '%s_to_%s' % (u.id, s)
assign_user_to_server_vars = mdl.binary_var_matrix(users, servers, user_server_pair_namer)
max_sleeping_workload = mdl.integer_var(name="max_sleeping_processes")
In [ ]:
def _is_migration(user, server):
""" Returns True if server is not the user's current
Used in setup of constraints.
"""
return server != user.current_server
In [ ]:
mdl.add_constraints(
mdl.sum(assign_user_to_server_vars[u, s] * u.running for u in users) <= max_processes_per_server
for s in servers)
mdl.print_information()
In [ ]:
# each assignment var <u, s> is <= active_server(s)
for s in servers:
for u in users:
ct_name = 'ct_assign_to_active_{0!s}_{1!s}'.format(u, s)
mdl.add_constraint(assign_user_to_server_vars[u, s] <= active_var_by_server[s], ct_name)
In [ ]:
# sum of assignment vars for (u, all s in servers) == 1
for u in users:
ct_name = 'ct_unique_server_%s' % (u[0])
mdl.add_constraint(mdl.sum((assign_user_to_server_vars[u, s] for s in servers)) == 1.0, ct_name)
mdl.print_information()
In [ ]:
number_of_active_servers = mdl.sum((active_var_by_server[svr] for svr in servers))
mdl.add_kpi(number_of_active_servers, "Number of active servers")
number_of_migrations = mdl.sum(
assign_user_to_server_vars[u, s] for u in users for s in servers if _is_migration(u, s))
mdl.add_kpi(number_of_migrations, "Total number of migrations")
for s in servers:
ct_name = 'ct_define_max_sleeping_%s' % s
mdl.add_constraint(
mdl.sum(
assign_user_to_server_vars[u, s] * u.sleeping for u in users) <= max_sleeping_workload,
ct_name)
mdl.add_kpi(max_sleeping_workload, "Max sleeping workload")
mdl.print_information()
In [ ]:
# Set objective function
mdl.minimize(number_of_active_servers)
mdl.print_information()
In [ ]:
# build an ordered sequence of goals
ordered_kpi_keywords = ["servers", "migrations", "sleeping"]
ordered_goals = [mdl.kpi_by_name(k) for k in ordered_kpi_keywords]
mdl.solve_lexicographic(ordered_goals)
mdl.report()
In [ ]:
active_servers = sorted([s for s in servers if active_var_by_server[s].solution_value == 1])
print("Active Servers: {}".format(active_servers))
print("*** User assignment ***")
for (u, s) in sorted(assign_user_to_server_vars):
if assign_user_to_server_vars[(u, s)].solution_value == 1:
print("{} uses {}, migration: {}".format(u, s, "yes" if _is_migration(u, s) else "no"))
print("*** Servers sleeping processes ***")
for s in active_servers:
sleeping = sum(assign_user_to_server_vars[u, s].solution_value * u.sleeping for u in users)
print("Server: {} #sleeping={}".format(s, sleeping))
Copyright © 2017-2019 IBM. IPLA licensed Sample Materials.
In [ ]: