Skip to content

Instantly share code, notes, and snippets.

@WillianFuks
Last active November 17, 2021 21:46
Show Gist options
  • Save WillianFuks/1e372264d4bacc3de067d6956e83ecc6 to your computer and use it in GitHub Desktop.
Save WillianFuks/1e372264d4bacc3de067d6956e83ecc6 to your computer and use it in GitHub Desktop.
def optimize(data_models, train_limit, ob_cost_factor=2, container_cost_factor=10):
results = {}
containers = data_models['containers']
shippers = data_models['shippers']
orig_ports = data_models['orig_port']
transp = data_models['transp']
transp_train_map = data_models['transp_train_map']
shipper_trade_spaces = data_models['shipper_trade_spaces']
overbooking_spaces = shipper_trade_spaces.matrix
dest_shipper_map = data_models['dest_shipper_map']
orig_transp_map = data_models['orig_transp_map']
dest_shipper_and_orig = data_models['dest_shipper_and_orig']
trade_shipper_and_trade_map = data_models['trade_shipper_and_trade_map']
shipper_shipper_and_trade_map = data_models['shipper_shipper_and_trade_map']
dest_trade_map = data_models['dest_trade_map']
orig_shipper_map = data_models['orig_shipper_map']
orig_shipper_and_orig_map = data_models['orig_shipper_and_orig_map']
shipper_shipper_and_orig_map = data_models['shipper_shipper_and_orig_map']
factory_orig_and_transp_cost = data_models['factory_orig_and_transp_cost']
factory_orig_and_transp_map = data_models['factory_orig_and_transp_map']
orig_orig_and_transp_map = data_models['orig_orig_and_transp_map']
transp_orig_and_transp_map = data_models['transp_orig_and_transp_map']
dests_arr = np.asarray(containers.matrix[:, 1].todense()).flatten().astype(np.int32)
dest_trades_arr = dest_trade_map.matrix[dests_arr]
# This matrix is a mapping from trades to shippers and trades. This allows us to
# convert everything to spaces later on as spaces are defined at the shipper and
# trade level.
trade_shipper_trade_matrix = dest_trades_arr @ trade_shipper_and_trade_map.matrix
factories_arr = np.asarray(containers.matrix[:, 0].todense()).flatten().astype(
np.int32)
# Each factory have only a few available options for transportation and which origins
# it may attend. This variable is used to make sure the origin and transport chosen
# by the algorithm are available for the factory of each container
factories_origs_transp_arr = factory_orig_and_transp_map.matrix[factories_arr]
orig_and_transp_costs_matrix = factory_orig_and_transp_cost.matrix[factories_arr]
container_costs_arr = np.asarray(containers.matrix[:, 2].todense()).flatten()
n_containers = len(containers)
n_shippers = len(shippers)
n_origins = len(orig_ports)
n_transp = len(transp)
# cvxpy variables.
x_shippers = cx.Variable((n_containers, n_shippers), boolean=True, name='Shippers')
x_ob_shippers = cx.Variable((n_containers, n_shippers), boolean=True,
name='OverBooking Shippers')
x_origins = cx.Variable((n_containers, n_origins), boolean=True, name='Origins')
x_transp = cx.Variable((n_containers, n_transp), boolean=True, name='Transports')
# cvxpy "AND" variables.
y_shipper_and_origin = cx.Variable(
(n_containers, shipper_shipper_and_orig_map.matrix.shape[1]),
boolean=True,
name='Shippers AND Origins'
)
y_shipper_ob_and_origin = cx.Variable(
(n_containers, shipper_shipper_and_orig_map.matrix.shape[1]),
boolean=True,
name='Shippers Overbooking AND Origins'
)
y_origin_and_transp = cx.Variable(
(n_containers, orig_orig_and_transp_map.matrix.shape[1]),
boolean=True,
name='Orig Ports AND Transportation'
)
constraints = []
# x_shippers constraints
shipper_shipper_trade_arr = x_shippers @ shipper_shipper_and_trade_map.matrix
shipper_trade_consumed = cx.sum(
cx.multiply(trade_shipper_trade_matrix, shipper_shipper_trade_arr),
axis=0, keepdims=True
)
constr00 = shipper_trade_consumed <= shipper_trade_spaces.matrix
constr01 = cx.sum(x_shippers + x_ob_shippers, axis=1) <= 1
constr02 = (x_shippers + x_ob_shippers) <= dest_shipper_map.matrix[dests_arr]
constr03 = cx.sum(shipper_trade_consumed) <= n_containers
constraints.extend([constr00, constr01, constr02, constr03])
# x_origins constraints
constr10 = cx.sum(x_origins, axis=1) <= 1
constr11 = (x_shippers + x_ob_shippers) <= x_origins @ orig_shipper_map.matrix
constr12 = cx.sum(x_shippers + x_ob_shippers, axis=1) == cx.sum(x_origins, axis=1)
constraints.extend([constr10, constr11, constr12])
# x_ob_shippers constraints
constr20 = cx.sum(x_ob_shippers, axis=1) <= 1
constr21 = cx.sum(x_shippers + x_ob_shippers) <= n_containers
shipper_ob_shipper_ob_trade_arr = (
x_ob_shippers @ shipper_shipper_and_trade_map.matrix
)
shipper_ob_trade_consumed = cx.sum(
cx.multiply(trade_shipper_trade_matrix, shipper_ob_shipper_ob_trade_arr),
axis=0, keepdims=True
)
shipper_ob_trade_indices = cx.max(
cx.multiply(trade_shipper_trade_matrix, shipper_ob_shipper_ob_trade_arr),
axis=0, keepdims=True
)
constr22 = shipper_ob_trade_consumed <= overbooking_spaces
constr23 = (
shipper_trade_consumed >=
cx.multiply(shipper_trade_spaces.matrix, cx.max(shipper_ob_trade_indices, axis=0,
keepdims=True))
)
constraints.extend([constr20, constr21, constr22, constr23])
# y_shipper_and_orig constraints.
x1 = x_shippers @ shipper_shipper_and_orig_map.matrix
x2 = x_origins @ orig_shipper_and_orig_map.matrix
constr30 = y_shipper_and_origin >= x1 + x2 - 1
constr31 = y_shipper_and_origin <= x1
constr32 = y_shipper_and_origin <= x2
constr33 = cx.sum(y_shipper_and_origin, axis=1) == cx.sum(x_shippers, axis=1)
constr34 = y_shipper_and_origin >= 0
x1 = x_ob_shippers @ shipper_shipper_and_orig_map.matrix
constr35 = y_shipper_ob_and_origin >= x1 + x2 - 1
constr36 = y_shipper_ob_and_origin <= x1
constr37 = y_shipper_ob_and_origin <= x2
constr38 = cx.sum(y_shipper_ob_and_origin, axis=1) == cx.sum(x_ob_shippers, axis=1)
constr39 = y_shipper_ob_and_origin >= 0
constraints.extend([constr30, constr31, constr32, constr33, constr34, constr35,
constr36, constr37, constr38, constr39])
# x_transp constraints.
constr40 = cx.sum(x_origins, axis=1) == cx.sum(x_transp, axis=1)
constr41 = x_transp <= x_origins @ orig_transp_map.matrix
constr42 = cx.sum(x_transp @ transp_train_map.matrix) <= train_limit
constraints.extend([constr40, constr41, constr42])
# y_origin_and_transp
x1 = x_origins @ orig_orig_and_transp_map.matrix
x2 = x_transp @ transp_orig_and_transp_map.matrix
constr50 = y_origin_and_transp >= x1 + x2 - 1
constr51 = y_origin_and_transp <= x1
constr52 = y_origin_and_transp <= x2
constr53 = cx.sum(y_origin_and_transp, axis=1) == cx.sum(x_origins, axis=1)
constr54 = y_origin_and_transp >= 0
constr55 = cx.sum(
cx.multiply(factories_origs_transp_arr, y_origin_and_transp),
axis=1) == cx.sum(x_origins, axis=1)
constraints.extend([constr50, constr51, constr52, constr53, constr54, constr55])
cost = (
cx.sum(cx.multiply(
y_shipper_and_origin,
dest_shipper_and_orig.matrix[dests_arr]
)) +
cx.sum(
cx.multiply(y_shipper_ob_and_origin,
dest_shipper_and_orig.matrix[dests_arr] * ob_cost_factor)
) +
cx.sum(
cx.multiply((1 - cx.sum(x_shippers + x_ob_shippers, axis=1)),
container_costs_arr * container_cost_factor)) +
cx.sum(
cx.multiply(y_origin_and_transp, orig_and_transp_costs_matrix))
)
objective = cx.Minimize(cost)
problem = cx.Problem(objective, constraints)
problem.solve()
print(problem.solution)
print(time() - t0)
print('y_origin_and_transp ', y_origin_and_transp.value)
print('y_shipper_and_origin ', y_shipper_and_origin.value)
results['x_shippers'] = x_shippers.value
results['x_ob_shippers'] = x_ob_shippers.value
results['x_origins'] = x_origins.value
results['x_transp'] = x_transp.value
return results
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment