def get_maintenance_scheme_pulp_optimization(de_capacity, dm_capacity, data_centers) -> tuple: """ Uses PuLP library to find minimal values of equations """ variables = list() for i in range(len(data_centers) * 2): variable = pulp.LpVariable('x{}'.format(i), lowBound=0, cat='Integer') variables.append(variable) managers = variables[::2] engineers = variables[1::2] problem = pulp.LpProblem("Minimize number of engineers", pulp.LpMinimize) problem += sum(engineers), 'Z' problem += sum(managers) == 1 for i, data_center in enumerate(data_centers): problem += dm_capacity * managers[i] + de_capacity * engineers[ i] >= data_center['servers'] problem.solve() for variable in managers: if variable.value(): data_center_id = int( int(re.search(r'\d+', str(variable)).group()) / 2) engineer_list = [x.value() for x in engineers] return int(sum(engineer_list)), data_centers[data_center_id]['name']
def host_solve(self, hosts, instance_uuids, request_spec, filter_properties): """This method returns a list of tuples - (host, instance_uuid) that are returned by the solver. Here the assumption is that all instance_uuids have the same requirement as specified in filter_properties. """ host_instance_tuples_list = [] if instance_uuids: num_instances = len(instance_uuids) else: num_instances = request_spec.get('num_instances', 1) #Setting a unset uuid string for each instance. instance_uuids = [ 'unset_uuid' + str(i) for i in xrange(num_instances) ] num_hosts = len(hosts) LOG.debug(_("All Hosts: %s") % [h.host for h in hosts]) for host in hosts: LOG.debug(_("Host state: %s") % host) # Create dictionaries mapping host/instance IDs to hosts/instances. host_ids = ['Host' + str(i) for i in range(num_hosts)] host_id_dict = dict(zip(host_ids, hosts)) instance_ids = ['Instance' + str(i) for i in range(num_instances)] instance_id_dict = dict(zip(instance_ids, instance_uuids)) # Create the 'prob' variable to contain the problem data. prob = pulp.LpProblem("Host Instance Scheduler Problem", constants.LpMinimize) # Create the 'variables' matrix to contain the referenced variables. variables = [[ pulp.LpVariable("IA" + "_Host" + str(i) + "_Instance" + str(j), 0, 1, constants.LpInteger) for j in range(num_instances) ] for i in range(num_hosts)] # Get costs and constraints and formulate the linear problem. self.cost_objects = [cost() for cost in self.cost_classes] self.constraint_objects = [ constraint(variables, hosts, instance_uuids, request_spec, filter_properties) for constraint in self.constraint_classes ] costs = [[0 for j in range(num_instances)] for i in range(num_hosts)] for cost_object in self.cost_objects: cost = cost_object.get_cost_matrix(hosts, instance_uuids, request_spec, filter_properties) cost = cost_object.normalize_cost_matrix(cost, 0.0, 1.0) weight = float(self.cost_weights[cost_object.__class__.__name__]) costs = [[ costs[i][j] + weight * cost[i][j] for j in range(num_instances) ] for i in range(num_hosts)] prob += (pulp.lpSum([ costs[i][j] * variables[i][j] for i in range(num_hosts) for j in range(num_instances) ]), "Sum_of_Host_Instance_Scheduling_Costs") for constraint_object in self.constraint_objects: coefficient_vectors = constraint_object.get_coefficient_vectors( variables, hosts, instance_uuids, request_spec, filter_properties) variable_vectors = constraint_object.get_variable_vectors( variables, hosts, instance_uuids, request_spec, filter_properties) operations = constraint_object.get_operations( variables, hosts, instance_uuids, request_spec, filter_properties) for i in range(len(operations)): operation = operations[i] len_vector = len(variable_vectors[i]) prob += (operation( pulp.lpSum([ coefficient_vectors[i][j] * variable_vectors[i][j] for j in range(len_vector) ])), "Costraint_Name_%s" % constraint_object.__class__.__name__ + "_No._%s" % i) # The problem is solved using PULP's choice of Solver. prob.solve() # Create host-instance tuples from the solutions. if pulp.LpStatus[prob.status] == 'Optimal': for v in prob.variables(): if v.name.startswith('IA'): (host_id, instance_id) = v.name.lstrip('IA').lstrip('_').split('_') if v.varValue == 1.0: host_instance_tuples_list.append( (host_id_dict[host_id], instance_id_dict[instance_id])) return host_instance_tuples_list
def solve(self, hosts, filter_properties): """This method returns a list of tuples - (host, instance_uuid) that are returned by the solver. Here the assumption is that all instance_uuids have the same requirement as specified in filter_properties. """ host_instance_combinations = [] num_instances = filter_properties['num_instances'] num_hosts = len(hosts) instance_uuids = filter_properties.get('instance_uuids') or [ '(unknown_uuid)' + str(i) for i in xrange(num_instances) ] filter_properties.setdefault('solver_cache', {}) filter_properties['solver_cache'].update({ 'cost_matrix': [], 'constraint_matrix': [] }) cost_matrix = self._get_cost_matrix(hosts, filter_properties) cost_matrix = self._adjust_cost_matrix(cost_matrix) constraint_matrix = self._get_constraint_matrix( hosts, filter_properties) # Create dictionaries mapping temporary host/instance keys to # hosts/instance_uuids. These temorary keys are to be used in the # solving process since we need a convention of lp variable names. host_keys = ['Host' + str(i) for i in xrange(num_hosts)] host_key_map = dict(zip(host_keys, hosts)) instance_num_keys = [ 'InstanceNum' + str(i) for i in xrange(num_instances + 1) ] instance_num_key_map = dict( zip(instance_num_keys, xrange(num_instances + 1))) # create the pulp variables variable_matrix = [[ pulp.LpVariable('HI_' + host_key + '_' + instance_num_key, 0, 1, constants.LpInteger) for instance_num_key in instance_num_keys ] for host_key in host_keys] # create the 'prob' variable to contain the problem data. prob = pulp.LpProblem("Host Instance Scheduler Problem", constants.LpMinimize) # add cost function to pulp solver cost_variables = [ variable_matrix[i][j] for i in xrange(num_hosts) for j in xrange(num_instances + 1) ] cost_coefficients = [ cost_matrix[i][j] for i in xrange(num_hosts) for j in xrange(num_instances + 1) ] prob += (pulp.lpSum([ cost_coefficients[i] * cost_variables[i] for i in xrange(len(cost_variables)) ]), "Sum_Costs") # add constraints to pulp solver for i in xrange(num_hosts): for j in xrange(num_instances + 1): if constraint_matrix[i][j] is False: prob += (variable_matrix[i][j] == 0, "Cons_Host_%s" % i + "_NumInst_%s" % j) # add additional constraints to ensure the problem is valid # (1) non-trivial solution: number of all instances == that requested prob += (pulp.lpSum([ variable_matrix[i][j] * j for i in xrange(num_hosts) for j in xrange(num_instances + 1) ]) == num_instances, "NonTrivialCons") # (2) valid solution: each host is assigned 1 num-instances value for i in xrange(num_hosts): prob += (pulp.lpSum([ variable_matrix[i][j] for j in xrange(num_instances + 1) ]) == 1, "ValidCons_Host_%s" % i) # The problem is solved using PULP's choice of Solver. prob.solve( pulp_solver_classes.PULP_CBC_CMD( maxSeconds=CONF.solver_scheduler.pulp_solver_timeout_seconds)) # Create host-instance tuples from the solutions. if pulp.LpStatus[prob.status] == 'Optimal': num_insts_on_host = {} for v in prob.variables(): if v.name.startswith('HI'): (host_key, instance_num_key ) = v.name.lstrip('HI').lstrip('_').split('_') if v.varValue == 1: num_insts_on_host[host_key] = ( instance_num_key_map[instance_num_key]) instances_iter = iter(instance_uuids) for host_key in host_keys: num_insts_on_this_host = num_insts_on_host.get(host_key, 0) for i in xrange(num_insts_on_this_host): host_instance_combinations.append( (host_key_map[host_key], instances_iter.next())) else: LOG.warn( _LW("Pulp solver didnot find optimal solution! " "reason: %s"), pulp.LpStatus[prob.status]) host_instance_combinations = [] return host_instance_combinations
def host_solve(self, hosts, instance_uuids, request_spec, filter_properties): """This method returns a list of tuples - (host, instance_uuid) that are returned by the solver. Here the assumption is that all instance_uuids have the same requirement as specified in filter_properties """ host_instance_tuples_list = [] if instance_uuids: num_instances = len(instance_uuids) else: num_instances = request_spec.get('num_instances', 1) instance_uuids = [ 'unset_uuid%s' % i for i in xrange(num_instances) ] num_hosts = len(hosts) host_ids = ['Host%s' % i for i in range(num_hosts)] LOG.debug(_("All Hosts: %s") % [h.host for h in hosts]) for host in hosts: LOG.debug(_("Host state: %s") % host) host_id_dict = dict(zip(host_ids, hosts)) instances = ['Instance%s' % i for i in range(num_instances)] instance_id_dict = dict(zip(instances, instance_uuids)) # supply is a dictionary for the number of units of # resource for each Host. # Currently using only the disk_mb and memory_mb # as the two resources to satisfy. Need to eventually be able to # plug-in different resources. An example supply dictionary: # supply = {"Host1": [1000, 1000], # "Host2": [4000, 1000]} supply = dict((host_ids[i], [ self._get_usable_disk_mb(hosts[i]), self._get_usable_memory_mb(hosts[i]), ]) for i in range(len(host_ids))) number_of_resource_types_per_host = 2 required_disk_mb = self._get_required_disk_mb(filter_properties) required_memory_mb = self._get_required_memory_mb(filter_properties) # demand is a dictionary for the number of # units of resource required for each Instance. # An example demand dictionary: # demand = {"Instance0":[200, 300], # "Instance1":[900, 100], # "Instance2":[1800, 200], # "Instance3":[200, 300], # "Instance4":[700, 800], } # However for the current scenario, all instances to be scheduled # per request have the same requirements. Need to eventually # to support requests to specify different instance requirements demand = dict((instances[i], [ required_disk_mb, required_memory_mb, ]) for i in range(num_instances)) # Creates a list of costs of each Host-Instance assignment # Currently just like the nova.scheduler.weights.ram.RAMWeigher, # using host_state.free_ram_mb * ram_weight_multiplier # as the cost. A negative ram_weight_multiplier means to stack, # vs spread. # An example costs list: # costs = [ # Instances # # 1 2 3 4 5 # [2, 4, 5, 2, 1], # A Hosts # [3, 1, 3, 2, 3] # B # ] # Multiplying -1 as we want to use the same behavior of # ram_weight_multiplier as used by ram weigher. costs = [[ -1 * host.free_ram_mb * CONF.ram_weight_multiplier for i in range(num_instances) ] for host in hosts] costs = pulp.makeDict([host_ids, instances], costs, 0) # The PULP LP problem variable used to add all the problem data prob = pulp.LpProblem("Host Instance Scheduler Problem", constants.LpMinimize) all_host_instance_tuples = [(w, b) for w in host_ids for b in instances] vars = pulp.LpVariable.dicts("IA", (host_ids, instances), 0, 1, constants.LpInteger) # The objective function is added to 'prob' first prob += (pulp.lpSum([ vars[w][b] * costs[w][b] for (w, b) in all_host_instance_tuples ]), "Sum_of_Host_Instance_Scheduling_Costs") # The supply maximum constraints are added to # prob for each supply node (Host) for w in host_ids: for i in range(number_of_resource_types_per_host): prob += (pulp.lpSum( [vars[w][b] * demand[b][i] for b in instances]) <= supply[w][i], "Sum_of_Resource_%s" % i + "_provided_by_Host_%s" % w) # The number of Hosts required per Instance, in this case it is only 1 for b in instances: prob += (pulp.lpSum([vars[w][b] for w in host_ids]) == 1, "Sum_of_Instance_Assignment%s" % b) # The demand minimum constraints are added to prob for # each demand node (Instance) for b in instances: for j in range(number_of_resource_types_per_host): prob += ( pulp.lpSum([vars[w][b] * demand[b][j] for w in host_ids]) >= demand[b][j], "Sum_of_Resource_%s" % j + "_required_by_Instance_%s" % b) # The problem is solved using PuLP's choice of Solver prob.solve() if pulp.LpStatus[prob.status] == 'Optimal': for v in prob.variables(): if v.name.startswith('IA'): (host_id, instance_id) = v.name.lstrip('IA').lstrip('_').split('_') if v.varValue == 1.0: host_instance_tuples_list.append( (host_id_dict[host_id], instance_id_dict[instance_id])) return host_instance_tuples_list
from pulp import pulp # Ejemplo del problema de transporte utilizando PuLP # Variable prob que contiene los datos del problema prob = pulp.LpProblem("Problema de distribución", pulp.LpMinimize) # Creamos lista de Centros de Distribución o nodos de oferta Centros_Distribucion = ["CEDI A", "CEDI B"] # diccionario con la capacidad de oferta de cada CEDI oferta = {"CEDI A": 10, "CEDI B": 20} # Creamos la lista de los bares o nodos de demanda plantas = ["Planta 1", "Planta 2", "Planta 3", "Planta 4", "Planta 5"] # diccionario con la capacidad de demanda de cada Planta demanda = { "Planta 1": 3, "Planta 2": 5, "Planta 3": 6, "Planta 4": 2, "Planta 5": 7, } # Lista con los costos de transporte de cada nodo costos = [ # Plantas # 1 2 3 4 5 [26250, 73500, 106750, 105000, 32900], # A CEDIS [28700, 18200, 40250, 129500, 37450] # B ]
def distribute(self, fairness=True, output=None): """ This method is responsible for actually solving the linear programming problem. It uses the data in the instance variables. The optional parameter **fairness** indicates if the solution should minimize individual effort. By default the solution will enforce this condition. An error will be raised in case the data has inconsistencies. """ # Validate the problem variables self._validate() # State problem problem = pulp.LpProblem("Fair Distribution Problem", pulp.LpMinimize) # Prepare variables targets_objects = {} for (t, target) in enumerate(self._targets): for (o, object) in enumerate(self._objects): variable = pulp.LpVariable( 'x' + str(t) + str(o), lowBound=0, cat='Binary') position = {'target': t, 'object': o} targets_objects['x' + str(t) + str(o)] = (variable, position) # Generate linear expression for self._weights (Summation #1) weights = [(variable, self._weights[weight_position['target']][weight_position['object']]) for (variable, weight_position) in targets_objects.values()] weight_expression = pulp.LpAffineExpression(weights) # Generate linear expression for effort distribution (Summation #2) weight_diff_vars = [] if fairness: total_targets = len(self._targets) for (t, target) in enumerate(self._targets): weight_diff_aux_variable = pulp.LpVariable( 'weight_diff_'+str(t), lowBound=0) weight_diff_vars.append(weight_diff_aux_variable) negative_effort_diff_weights = [] positive_effort_diff_weights = [] negative_factor = -1 * total_targets positive_factor = 1 * total_targets for (o, object) in enumerate(self._objects): id = 'x' + str(t) + str(o) negative_effort_diff_weights.append( (targets_objects[id][0], negative_factor * self._weights[t][o])) positive_effort_diff_weights.append( (targets_objects[id][0], positive_factor * self._weights[t][o])) negative_effort_diff = pulp.LpAffineExpression( negative_effort_diff_weights) + weight_expression positive_effort_diff = pulp.LpAffineExpression( positive_effort_diff_weights) - weight_expression problem += negative_effort_diff <= weight_diff_aux_variable, 'abs negative effort diff ' + \ str(t) problem += positive_effort_diff <= weight_diff_aux_variable, 'abs positive effort diff ' + \ str(t) # Constraints - Each task must be done for (o, object) in enumerate(self._objects): constraints = [] for (t, target) in enumerate(self._targets): constraints.append(targets_objects['x' + str(t) + str(o)][0]) problem += pulp.lpSum(constraints) == 1, 'Task ' + \ str(o) + ' must be done' # Set objective function problem += weight_expression + \ pulp.lpSum(weight_diff_vars), "obj" if output: problem.writeLP(output) problem.solve() # Build output data = {} for v in filter(lambda x: x.varValue > 0, problem.variables()): if v.name not in targets_objects: continue position = targets_objects[v.name][1] target = self._targets[position['target']] object = self._objects[position['object']] if target not in data: data[target] = [] data[target].append(object) return data