I'm trying to optimize a 10x5 matrix to maximize a return value y, using mystic. There are 2 types of constraints that I need to include:
- Each element must be in between a min and max range
- Some elements must equal specific values (provided in a dictionary)
Not sure why the optimized result includes negative values as I have already made a constraint for that (constraint no.1) and the return y value seems to be incredibly small?
import random
import pandas as pd
import numpy as np
import mystic.symbolic as ms
import mystic as my
import scipy.optimize as so
# Specifiying constraints
element_low_lim = 0
element_hi_lim = 1000
total_matrix_min_sum = 0
total_matrix_max_sum = 220000
# Create an input matrix where the values must equal a specific amount
matrix_in = np.zeros((52,5))
matrix_in[0,0] = 100
def constraints_func(element_low_lim, element_hi_lim, total_matrix_min_sum, total_matrix_max_sum,element_vals_dict):
var_num = ['x'+str(i) for i in range(260)]
#creating element bounds constraints
constraint_bound = ''
for var in var_num:
if var not in element_vals_dict:
constraint_bound += var + f' >= {element_low_lim}' + '\n' + var + f' <= {element_hi_lim}' + '\n'
#creating sum of all the elements constraint
constraint_matrix_sum = ' + '.join(var_num) + f' <= {total_matrix_max_sum}' + '\n' + ' + '.join(var_num) + f' >= {total_matrix_min_sum}'
#creating element constraints
constraint_elements = '\n'.join([var+' == '+str(element_vals_dict[var]) for var in element_vals_dict])
# bundle all constraints
constraint_equations = constraint_bound.lstrip() + constraint_matrix_sum.lstrip() + constraint_column_sum.lstrip() + constraint_elements.lstrip()
return constraint_equations
equations = constraints_func(element_low_lim, element_hi_lim, total_matrix_min_sum, total_matrix_max_sum, column_sum_min_lst, column_sum_max_lst, element_vals_dict)
equations = ms.simplify(equations, all=True)
constrain = ms.generate_constraint(ms.generate_solvers(equations), join=my.constraints.and_)
# Define Objective function
def obj_func(matrix):
return np.sum(output_matrix)
mon = my.monitors.VerboseMonitor(1)
objs = []
def callback(x):
kx = constrain(x)
y = -obj_func(x)
mon(kx, y)
objs.append(y)
# create a starting matrix
start_matrix = [random.randint(0,3) for i in range(200)]
# run optimizer
solution = so.minimize(obj_func, start_matrix, method='SLSQP', tol=0.01, options={'disp': True, 'maxiter':100}, callback=callback)
The issue you are running into is that
mysticcould use some help in simplifying the equations so that they are most likely to succeed when imposing the constraints.You have
x0,x1, andx2as fixed. However, in your sum constraints, simplify will (by default) isolate the first variable in the list on the LHS -- where the isolated variable is the one that the constraints adjust. So, havingx0fixed, and then expecting it to be adjusted to resolve the sum constraints is a bad idea.mysticwill attempt to resolve the constraints, but fail and give up.What you want to do is isolate the "non-fixed" variables in the sum constraints. I've rewritten your code to do that.
Broken up the constraints functions to make them easier to deal with in simplify.
I'll build the constraints now, but as there are some unnecessary constraints in
eqn3, I'm going to get rid of them. Essentially,mergeidentifies thatCON <= 0andCON >= 0, together, don't apply any constraint.Now, I need to isolate something other than
x0,x1, orx2... so I could either go back and not include the above three variable in the relevant equations until after simplification, or I have to work a bit to isolate something else. I'll sloppily do the isolation however I can with what I have.We then combine, and generate the constraints.
Set up the solver...
then solve...
Then print the solution:
Hopefully, that looks right.