I'm trying to design a constraint that is based on optimizing groups/buckets of elements rather than just individual elements. Here is a list of all the constraints I have:
- Total sum of all the elements must equal a certain value
- Total sum of each product (column sum of elements) must equal a certain value
- Bucket of size 'x', where every element in the bucket must be the same
Here is an example of what I'm after:
Constraints
Total Budget = 10000
Product-A Budget = 2500
Product-B Budget = 7500
Product-A Bucket Size = 3
Product-B Bucket Size = 2
Below is an illustration of the buckets (have placed them randomly, optimizer should decide where to best place the bucket within each column):
Product-A Product-B
_______
Week 1 | |
Week 2 | | ________
Week 3 |_______| | |
Week 4 |________|
Week 5
After passing the constraints to the optimizer, I want to the optimizer to allocate elements like this (every element inside the bucket should be equal):
Desired Optimizer Output
Product-A Product-B
_______
Week 1 | 500 | 150
Week 2 | 500 | __2350__
Week 3 |__500__| | 1000 |
Week 4 700 |__1000__|
Week 5 300 3000
Here is my attempt at trying to create the bucket constraint. I've used a simple, dummy objective function for demo purposes:
# Import Libraries
import pandas as pd
import numpy as np
import scipy.optimize as so
import random
# Define Objective function (Maximization)
def obj_func(matrix):
return -np.sum(matrix)
# Create optimizer function
def optimizer_result(tot_budget, col_budget_list, bucket_size_list):
# Create constraint 1) - total matrix sum range
constraints_list = [{'type': 'eq', 'fun': lambda x: np.sum(x) - tot_budget},
{'type': 'eq', 'fun': lambda x: (sum(x[i] for i in range(0, 10, 5)) - col_budget_list[0])},
{'type': 'eq', 'fun': lambda x: (sum(x[i] for i in range(1, 10, 5)) - col_budget_list[1])},
{'type': 'eq', 'fun': lambda x, bucket_size_list[0]: [item for item in x for i in range(bucket_size_list[0])]},
{'type': 'eq', 'fun': lambda x, bucket_size_list[1]: [item for item in x for i in range(bucket_size_list[1])]}]
# Create an inital matrix
start_matrix = [random.randint(0, 3) for i in range(0, 10)]
# Run optimizer
optimizer_solution = so.minimize(obj_func, start_matrix, method='SLSQP', bounds=[(0, tot_budget)] * 10,
tol=0.01,
options={'disp': True, 'maxiter': 100}, constraints=constraints_list)
return optimizer_solution
# Initalise constraints
tot_budget = 10000
col_budget_list = [2500, 7500]
bucket_size_list = [3, 2]
# Run Optimizer
y = optimizer_result(tot_budget, col_budget_list, bucket_size_list)
print(y)