Beispiel #1
0
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
Beispiel #3
0
    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
Beispiel #4
0
    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
Beispiel #5
0
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
]
Beispiel #6
0
    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