I am trying to figure out if it is possible to configure a specific problem as a mixed-integer program. I think I am able to structure it as a non-linear optimization problem, but would like to see if a MIP works better. The basic idea is that there are several systems comprised of multiple elements. Over time, the elements degrade and certain actions/decisions can be applied at periodic intervals. Each decision choice has a different cost and impact on element condition. The goal is to define the optimal set of decisions for each element at periodic intervals. However, the decisions must not allow an overall system to degrade below a minimally acceptable level, or to cost more than a budgeted amount for each period. Below is an example of how an example could be set up and how a single decision matrix would be evaluated: # 1. define parameters like # of systes, decision options, decision periods, types etc. # 2. define constraints # 3. model how the elements degrade over time, and costs for decisions # 4. model how elements will impact system metrics # 5. structure as MIP <- HOW TO DO THIS??: import random from random import choices import pandas as pd # define parameters # decision_options = list(range(0,4)) # defines number of decision options periods = list(range(0,6)) # defines number of decision periods system_types = ['A', 'B', 'C'] # type used for condition constraints # say we have 100 elements, each assigned to a system element_ids = list(range(0, 100)) # create initialized element condition values condition = choices(range(60,101), k=len(element_ids)) # assign types for each item system_type = choices(system_types, k=len(element_ids)) # assign value for each group value = random.sample(range(500, 20000), len(element_ids)) # create a dataframe with element, system type, condition, and element value information df = pd.DataFrame({'Element_ID': element_ids, 'System_Type': system_type, 'Condition': condition, "Value": value}) df # 2. define constraints # # create a dict where each type has a minimum allowable condition vals= [60, 50, 40] # System = A has a limit of 60, System B has a limit of 50... min_condition = dict(zip(system_types, vals)) # budget constraint for each period max_budget = [200000] * len(periods) # 3. model costs and degradation over time # create a function that sets a element cost based on decision def decision_cost(decision, value): cost = 0 match decision: case 0: cost = 0 # do nothing case 1: cost = value / 10 # do a little case 2: cost = value / 5 # do a little more case 3: cost = value / 2 # do a lot return(cost) # create a function that sets a element condition based on decision def decision_result(decision, condition): match decision: case 0: condition = condition # no improvement case 1: condition = min(condition*1.1, 100) # a little improvement case 2: condition = min(condition*1.2, 100) # a little more improvement case 3: condition = min(condition*1.5, 100) # a lot of improvement return(condition) # model element degradation # element loses 10% at each period def degrade_element(condition): new_condition = round(condition *0.9,0) return(new_condition) # 4. model how elements will impact system metrics # these are to be compared to the min_condition constraints # # system condition is the weighted-and-summed condition of the constituent elements def system_condition(df): system_types = sorted(df['System_Type'].unique()) system_condition = [0] * len(system_types) for i in range(len(system_types)): system_data = df[df['System_Type'] == system_types] system_condition = (system_data['Condition'] * system_data['Value']).sum() / system_data['Value'].sum() return(system_condition) def period_costs(new_df, periods): column_names = [f'Period_{p}' for p in periods] period_sums = [] for col in column_names: period_sums.append(new_df[col].sum()) return(period_sums) system_condition(df) # create a sample decision matrix: # row = element # column = period # cell value = decision import numpy as np # randomly initialize a decision matrix decision_matrix = np.random.randint(0, len(decision_options), size=(len(element_ids), max(periods)+1)) # example evaluation of a decision matrix # get cost and result dataframes for system/element results system_cost_df = system_result_df = pd.DataFrame(index=range(len(system_types)), columns=range(0, len(periods))) element_cost_df = element_result_df = pd.DataFrame(index=range(len(element_ids)), columns=range(0, len(periods))) def evaluate_decision_matrix(df, decision_matrix, periods): new_df = df #new_df['Cost'] = 0 # create a column to collect the cost for each period column_names = [f'Period_{p}' for p in periods] new_df = new_df.assign(**{col: 0 for col in column_names}) # for each period for i in range(0, len(periods)): # for each element for j in range(0, len(element_ids)): # execute decision decision = decision_matrix[j,i] element_value = new_df.iloc[j]['Value'] #element_cost_df.loc[j,i] = decision_cost(decision, element_value) #cost = decision_cost(decision, element_value) new_df.loc[j, column_names] = decision_cost(decision, element_value) # impact condition with decision current_condition = new_df.iloc[j]['Condition'] updated_condition = decision_result(decision, current_condition) new_df.loc[j, 'Condition'] = updated_condition # degrade element for next period degraded_condition = degrade_element(new_df.iloc[j]['Condition']) new_df.loc[j, 'Condition'] = degraded_condition return(new_df) # evaluate decision matrix new_df = evaluate_decision_matrix(df, decision_matrix, periods) # calculate system condition system_condition_values = system_condition(new_df) # check if system condition constraints are met # returns true if system condition constraints are met condition_check = [x > y for x, y in zip(system_condition_values, vals)] print(condition_check) # check if budget constaint is met costs = period_costs(new_df, periods) budget_check = [x < y for x, y in zip(costs, max_budget)] print(budget_check) As an example, the print(condition_check) command displays [True, True, True] indicating the system condition constraints are satisfied. But print(budget_check) shows [True, False, False, False, True, False] indicated 4 of the period budget constraints have been broken. Is it possible to structure this as an MIP in python and, if so, how would that be done? Continue reading...