Exemplo n.º 1
0
def computeLambda(state, maximumTime=1.0):
    activeAgents = state.getActiveAgents()
    agentNames = getUniqueNames(activeAgents, prefix="Agent ")
    classNames = getUniqueNames(state.getChoiceClasses(), prefix="Class ")
    choiceNames = getUniqueNames(state.getChoices(), prefix="")

    choiceVariables = LpVariable.dicts("p", choiceNames.values(), lowBound=0)
    lambdaVariable = LpVariable("l", lowBound=0.0, upBound=maximumTime)

    def createConstraints(problem, variable):
        problem += lpSum(choiceVariables) <= 1, "Distribution"
        for choiceClass, height in state.getCurrentClassHeights().items():
            problem += createLpSum(choiceClass, choiceNames, choiceVariables) >= \
                height, classNames[choiceClass] + " height"
        for agent in state.getActiveAgents():
            problem += createLpSum(state.getCurrentAgentChoiceClass(agent),
                                   choiceNames, choiceVariables) >= \
                state.getAgentHeight(agent) + variable * state.getAgentSpeed(agent), \
                agentNames[agent] + " push"

    problem = LpProblem("Lambda", LpMaximize)
    createConstraints(problem, lambdaVariable)
    problem.setObjective(lambdaVariable)
    checkPulpStatus(problem.solve(state.getSettings().getSolver()))
    lambdaOpt = lambdaVariable.value()

    bouncingAgents = []
    stringAgents = []  #TODO just a workaround; find better solution
    for currentAgent in activeAgents:
        print "Current agent: " + agentNames[
            currentAgent]  # name is off by one
        problem = LpProblem(agentNames[currentAgent], LpMaximize)
        createConstraints(problem, lambdaOpt)
        (choiceClass, height, speed) = state.getAgentData(currentAgent)
        # print "Choice Class: " + repr(choiceClass) + ",
        print "height: " + repr(height) + " , speed: " + repr(speed)
        problem.setObjective(
            createLpSum(choiceClass, choiceNames, choiceVariables) -
            lambdaOpt * speed - height)
        checkPulpStatus(problem.solve(state.getSettings().getSolver()))
        value = problem.objective.value()
        # print "value: " + repr(value) + "\n"
        if not state.getSettings().isNonnegative(value):
            raise ValueError(
                str(value) + " negative while determining bounce of " +
                repr(currentAgent) + " from " + repr(choiceClass) + "@" +
                str(height) + "/" + str(speed))
        if state.getSettings().isClose(value, 0):
            bouncingAgents.append(currentAgent)
            stringAgents.append(int(currentAgent.getName()))
        print "climbing time: " + repr(lambdaOpt) + ", bouncingAgents: " + str(
            stringAgents) + "\n"
        # print map(Agent.getName(), bouncingAgents)
    return (lambdaOpt, bouncingAgents)
Exemplo n.º 2
0
def computeLambda(state, maximumTime=1.0):
    activeAgents = state.getActiveAgents()
    agentNames = getUniqueNames(activeAgents, prefix="Agent ")
    classNames = getUniqueNames(state.getChoiceClasses(), prefix="Class ")
    choiceNames = getUniqueNames(state.getChoices(), prefix="")

    choiceVariables = LpVariable.dicts("p", choiceNames.values(), lowBound=0)
    lambdaVariable = LpVariable("l", lowBound=0.0, upBound=maximumTime)

    def createConstraints(problem, variable):
        problem += lpSum(choiceVariables) <= 1, "Distribution"
        for choiceClass, height in state.getCurrentClassHeights().items():
            problem += createLpSum(choiceClass, choiceNames, choiceVariables) >= \
                height, classNames[choiceClass] + " height"
        for agent in state.getActiveAgents():
            problem += createLpSum(state.getCurrentAgentChoiceClass(agent),
                                   choiceNames, choiceVariables) >= \
                state.getAgentHeight(agent) + variable * state.getAgentSpeed(agent), \
                agentNames[agent] + " push"

    problem = LpProblem("Lambda", LpMaximize)
    createConstraints(problem, lambdaVariable)
    problem.setObjective(lambdaVariable)
    checkPulpStatus(problem.solve(state.getSettings().getSolver()))
    lambdaOpt = lambdaVariable.value()

    bouncingAgents = []
    stringAgents = [] #TODO just a workaround; find better solution
    for currentAgent in activeAgents:
	print "Current agent: " + agentNames[currentAgent] # name is off by one
        problem = LpProblem(agentNames[currentAgent], LpMaximize)
        createConstraints(problem, lambdaOpt)
        (choiceClass, height, speed) = state.getAgentData(currentAgent)
	# print "Choice Class: " + repr(choiceClass) + ", 
	print "height: " + repr(height) + " , speed: " + repr(speed)
        problem.setObjective(createLpSum(choiceClass, choiceNames, choiceVariables)
                             - lambdaOpt * speed - height)
        checkPulpStatus(problem.solve(state.getSettings().getSolver()))
        value = problem.objective.value()
	# print "value: " + repr(value) + "\n"
        if not state.getSettings().isNonnegative(value):
            raise ValueError(str(value) + " negative while determining bounce of " +
                             repr(currentAgent) + " from " + repr(choiceClass) +
                             "@" + str(height) + "/" + str(speed))
        if state.getSettings().isClose(value, 0):
            bouncingAgents.append(currentAgent)
	    stringAgents.append(int(currentAgent.getName()))
	print "climbing time: " + repr(lambdaOpt) + ", bouncingAgents: " + str(stringAgents) + "\n"	
	# print map(Agent.getName(), bouncingAgents)
    return (lambdaOpt, bouncingAgents)
def findLottery(vote, classHeights, solverSettings):
    '''
    Returns a Lottery satisfying all constraints specified by the classHeights parameter

    @type vote: vote.society.Vote
    @type classHeights: dict(vote.society.ChoiceClass, float)
    @type solverSettings: vote.solver.settings.SolverSettings
    @rtype: vote.society.Lottery
    @raise ValueError: If the constraints are not satisfiable
    '''
    classNames = getUniqueNames(classHeights.keys(), prefix="Class ")
    choiceNames = getUniqueNames(vote.getChoices(), prefix="Choice ")

    problem = LpProblem("Lambda", LpMaximize)
    choiceVariables = LpVariable.dicts("p", choiceNames.values(), lowBound=0)

    problem += lpSum(choiceVariables) <= 1, "Distribution"
    for choiceClass, height in classHeights.items():
        problem += createLpSum(choiceClass, choiceNames, choiceVariables) >= \
            height, classNames[choiceClass] + " height"

    problem.setObjective(lpSum(choiceVariables.values()))
    checkPulpStatus(problem.solve(solverSettings.getSolver()))

    choiceValues = dict()
    for choice, choiceName in choiceNames.items():
        choiceValues[choice.getObject()] = choiceVariables[choiceName].value()
    return Lottery(choiceValues, solverSettings)
def findLottery(vote, classHeights, solverSettings):
    '''
    Returns a Lottery satisfying all constraints specified by the classHeights parameter

    @type vote: vote.society.Vote
    @type classHeights: dict(vote.society.ChoiceClass, float)
    @type solverSettings: vote.solver.settings.SolverSettings
    @rtype: vote.society.Lottery
    @raise ValueError: If the constraints are not satisfiable
    '''
    classNames = getUniqueNames(classHeights.keys(), prefix="Class ")
    choiceNames = getUniqueNames(vote.getChoices(), prefix="Choice ")

    problem = LpProblem("Lambda", LpMaximize)
    choiceVariables = LpVariable.dicts("p", choiceNames.values(), lowBound=0)

    problem += lpSum(choiceVariables) <= 1, "Distribution"
    for choiceClass, height in classHeights.items():
        problem += createLpSum(choiceClass, choiceNames, choiceVariables) >= \
            height, classNames[choiceClass] + " height"

    problem.setObjective(lpSum(choiceVariables.values()))
    checkPulpStatus(problem.solve(solverSettings.getSolver()))

    choiceValues = dict()
    for choice, choiceName in choiceNames.items():
        choiceValues[choice.getObject()] = choiceVariables[choiceName].value()
    return Lottery(choiceValues, solverSettings)
Exemplo n.º 5
0
    def genera_strutture_ausiliarie(self, dati) -> None:

        model = pl.LpProblem("CalcolaOrario", pl.LpMaximize)

        # set dei docenti che hanno insegnamenti tra i moduli da fissare in orario
        doc = set(m.get_matricola() for m in dati.get_moduli())
    
        # dict che mantiene l'associazione tra i moduli e i docenti titolari degli stessi
        tit = LpVariable.dicts("assign", [(m,d) for m in dati.get_moduli() for d in doc],
                            lowBound = 0,
                            upBound = 1,
                            cat = pl.LpInteger)
        
        # dict che mantiene l'associazione tra i moduli e le aule compatibili in relazione alla numerosità attesa
        cpt = LpVariable.dicts("assign", [(m,a) for m in dati.get_moduli() for a in dati.get_aule()],
                            lowBound = 0,
                            upBound = 1,
                            cat = pl.LpInteger)
        
        # valore 1 -> docente ha la titolarità di un dato modulo
        for m in dati.get_moduli():
            for d in doc:
                if d == m.get_matricola():
                    tit[m,d] = 1
                else:
                    tit[m,d] = 0
            
        # valore 1 -> l'aula è compatibile con la numerosità di un dato corso    
        for m in dati.get_moduli():
            for a in dati.get_aule():
                if a.get_tipo() == m.get_tipo_aula() and a.get_capienza() >= m.get_max_studenti():
                    cpt[m,a] = 1
                else:    
                    cpt[m,a] = 0
        
        # Le variabili skd valgono uno se per un dato slot di un dato giorno viene assegnata la lezione di un modulo 
        # di una dato corso in un data aula
        skd = LpVariable.dicts("assign", [(c,m,a,g,s) for c in dati.get_corsi() for m in dati.get_moduli() for a in dati.get_aule() 
                                                    for g in dati.get_giorni() for s in dati.get_slot()], 
                                lowBound = 0,
                                upBound = 1,
                                cat = pl.LpInteger)
                    
        return model, StruttureAusiliarie(doc, tit, cpt, skd)
Exemplo n.º 6
0
def computeLambda(state, maximumTime=1.0):
    '''
    @type state: SSRState
    @type maximumTime: float
    '''
    towerNames = getUniqueNames(state.getTowers(), prefix="T")
    choiceNames = getUniqueNames(state.getChoices(), prefix="")
    choiceVariables = LpVariable.dicts("p", choiceNames.values(), lowBound=0.0)
    lambdaVariable = LpVariable("l", lowBound=0.0, upBound=maximumTime)

    def createConstraints(problem, variable):
        problem += lpSum(choiceVariables) <= 1, "Distribution"
        for tower, towerName in towerNames.items():
            problem += createLpSum(tower.getChoiceClass(), choiceNames, choiceVariables) >= \
                tower.getHeight() + variable * \
                tower.getSpeed(), towerName

    problem = LpProblem("Lambda", LpMaximize)
    createConstraints(problem, lambdaVariable)
    problem.setObjective(lambdaVariable)
    checkPulpStatus(problem.solve(state.getSettings().getSolver()))
    lambdaOpt = lambdaVariable.value()

    freezingTowers = []
    for currentTower, towerName in towerNames.items():
        if currentTower.isFrozen():
            continue
        problem = LpProblem(towerName, LpMaximize)
        createConstraints(problem, lambdaOpt)
        problem.setObjective(
            createLpSum(currentTower.getChoiceClass(), choiceNames,
                        choiceVariables) -
            lambdaOpt * currentTower.getSpeed() - currentTower.getHeight())
        checkPulpStatus(problem.solve(state.getSettings().getSolver()))
        value = problem.objective.value()
        if not state.getSettings().isNonnegative(value):
            raise ValueError(
                str(value) + " negative while determining frozen state of " +
                repr(currentTower))
        if state.getSettings().isClose(problem.objective.value(), 0):
            freezingTowers.append(currentTower)

    return (lambdaOpt, frozenset(freezingTowers))
Exemplo n.º 7
0
def computeLambda(state, maximumTime=1.0):
    '''
    @type state: SSRState
    @type maximumTime: float
    '''
    towerNames = getUniqueNames(state.getTowers(), prefix="T")
    choiceNames = getUniqueNames(state.getChoices(), prefix="")
    choiceVariables = LpVariable.dicts("p", choiceNames.values(), lowBound=0.0)
    lambdaVariable = LpVariable("l", lowBound=0.0, upBound=maximumTime)

    def createConstraints(problem, variable):
        problem += lpSum(choiceVariables) <= 1, "Distribution"
        for tower, towerName in towerNames.items():
            problem += createLpSum(tower.getChoiceClass(), choiceNames, choiceVariables) >= \
                tower.getHeight() + variable * \
                tower.getSpeed(), towerName

    problem = LpProblem("Lambda", LpMaximize)
    createConstraints(problem, lambdaVariable)
    problem.setObjective(lambdaVariable)
    checkPulpStatus(problem.solve(state.getSettings().getSolver()))
    lambdaOpt = lambdaVariable.value()

    freezingTowers = []
    for currentTower, towerName in towerNames.items():
        if currentTower.isFrozen():
            continue
        problem = LpProblem(towerName, LpMaximize)
        createConstraints(problem, lambdaOpt)
        problem.setObjective(createLpSum(currentTower.getChoiceClass(), choiceNames, choiceVariables)
                             - lambdaOpt * currentTower.getSpeed() - currentTower.getHeight())
        checkPulpStatus(problem.solve(state.getSettings().getSolver()))
        value = problem.objective.value()
        if not state.getSettings().isNonnegative(value):
            raise ValueError(str(value) + " negative while determining frozen state of " +
                             repr(currentTower))
        if state.getSettings().isClose(problem.objective.value(), 0):
            freezingTowers.append(currentTower)

    return (lambdaOpt, frozenset(freezingTowers))
Exemplo n.º 8
0
def get_optimal_rackspace(cpu_usage, mem_usage, optimal=True, debug=False):
    """ 
    Calculates the price of optimal resources allocation over a certain time span (with monthly granularity). 
    Formulates the problem of satisfying user demand (in CPU and RAM) as an LP problem with a monetary objective function. 
    """    
    assert(len(cpu_usage) == len(mem_usage))
    
    prob = LpProblem("Rackspace cost optimization", LpMinimize)
    # variables
    ## 1h instances
    per_h_ondems = []
    for p in range(len(cpu_usage)):
        per_h_ondems += ["p %s ondem %s" %(p, i) for i in vms.keys()]
    
    category = LpInteger if optimal else LpContinuous
    vars = LpVariable.dicts("rackspace", per_h_ondems, 0, None, cat=category)
    
    # objective function
    prob += lpSum([vars[vm] * vms[vm.split(" ")[3]][0] for vm in per_h_ondems]), "Total cost of running the infra (wrt to CPU/RAM)" 
    
    # constraints    
    ## demand constraints
    for p in range(len(cpu_usage)):
        prob += lpSum([vars[vm] * vms[vm.split(" ")[3]][2] for vm in per_h_ondems if int(vm.split(" ")[1]) == p]) >= cpu_usage[p], "CU demand period %s" %p
        prob += lpSum([vars[vm] * vms[vm.split(" ")[3]][3] for vm in per_h_ondems if int(vm.split(" ")[1]) == p]) >= mem_usage[p], "RAM demand period %s" %p

    prob.solve()
    
    if debug:        
        for v in prob.variables():
            if v.varValue != 0.0:
                print v.name, "=", v.varValue

        print "Total Cost of the solution = ", value(prob.objective)
    
    return value(prob.objective)
Exemplo n.º 9
0
}

AllStudentsNumber = {
    "1": [53, 63, 65, 63, 94, 42, 58],
    "2": [13, 53, 69, 24, 42, 19, 48],
    "3": [40, 7, 10, 82, 64, 86, 2],
    "4": [51, 19, 36, 75, 92, 92, 14],
    "5": [73, 82, 22, 29, 72, 74, 77],
    "6": [73, 82, 22, 29, 72, 74, 77],
}

# 今回の問題では総和を最大化するので LpMaximize を指定する
problem = LpProblem('GroupAllScore', LpMaximize)

# 使用する変数
s1 = LpVariable('S1', 1, 6, 'Continuous')
s2 = LpVariable('S2', 1, 6, 'Continuous')
s3 = LpVariable('S3', 1, 6, 'Continuous')
s4 = LpVariable('S4', 1, 6, 'Continuous')
s5 = LpVariable('S5', 1, 6, 'Continuous')
s6 = LpVariable('S6', 1, 6, 'Continuous')
group1 = [s1, s2]
group2 = [s3, s4]
group3 = [s5, s6]
AllStudentsNumberDup = group1 + group2 + group3

# 目的関数
# score1 = AllStudents[group1[0]][0] + AllStudents[group1[1]][1]
# score2 = AllStudents[group2[0]][0] + AllStudents[group2[1]][1]
# score3 = AllStudents[group3[0]][0] + AllStudents[group3[1]][1]
# problem += score1 + score2 + score3
Exemplo n.º 10
0
from pulp.constants import LpMaximize
from pulp.pulp import LpProblem, LpVariable

# 今回の問題では総和を最大化するので LpMaximize を指定する
problem = LpProblem('Restaurant', LpMaximize)

# 使用する変数
x = LpVariable('X')
y = LpVariable('Y')

# 目的関数
problem += 30 * x + 25 * y

# 制約条件
problem += 0.5 * x + 0.25 * y <= 10  # ひき肉の総量は 3800g
problem += 0.5 * x + 0.75 * y <= 15  # 玉ねぎの総量は 2100g

# 解く
problem.solve()

# 結果表示
print('x: {x}'.format(x=x.value()))
print('y: {y}'.format(y=y.value()))
print(x.value() * y.value())

# class Node:
#     def __init__(self, x, y):
#         self.x = x
#         self.y = y
#         self.state = None
Exemplo n.º 11
0
def lp_model(cg: ComputationGraph,
             agentsdef: Iterable[AgentDef],
             footprint: Callable[[str], float],
             capacity: Callable[[str], float],
             route: Callable[[str, str], float],
             msg_load: Callable[[str, str], float],
             hosting_cost: Callable[[str, str], float]):

    comp_names = [n.name for n in cg.nodes]
    agt_names = [a.name for a in agentsdef]
    pb = LpProblem('ilp_compref', LpMinimize)

    # One binary variable xij for each (variable, agent) couple
    xs = LpVariable.dict('x', (comp_names, agt_names), cat=LpBinary)

    # One binary variable for computations c1 and c2, and agent a1 and a2
    betas = {}
    count = 0
    for a1, a2 in combinations(agt_names, 2):
        # Only create variables for couple c1, c2 if there is an edge in the
        # graph between these two computations.
        for l in cg.links:
            # As we support hypergraph, we may have more than 2 ends to a link
            for c1, c2 in combinations(l.nodes, 2):
                count += 2
                b = LpVariable('b_{}_{}_{}_{}'.format(c1, a1, c2, a2),
                               cat=LpBinary)
                betas[(c1, a1, c2, a2)] = b
                pb += b <= xs[(c1, a1)]
                pb += b <= xs[(c2, a2)]
                pb += b >= xs[(c2, a2)] + xs[(c1, a1)] - 1

                b = LpVariable('b_{}_{}_{}_{}'.format(c1, a2, c2, a1),
                               cat=LpBinary)
                betas[(c1, a2, c2, a1)] = b
                pb += b <= xs[(c2, a1)]
                pb += b <= xs[(c1, a2)]
                pb += b >= xs[(c1, a2)] + xs[(c2, a1)] - 1

    # Set objective: communication + hosting_cost
    pb += _objective(xs, betas, route, msg_load, hosting_cost), \
        'Communication costs and prefs'

    # Adding constraints:
    # Constraints: Memory capacity for all agents.
    for a in agt_names:
        pb += lpSum([footprint(i) * xs[i, a] for i in comp_names])\
              <= capacity(a), \
              'Agent {} capacity'.format(a)

    # Constraints: all computations must be hosted.
    for c in comp_names:
        pb += lpSum([xs[c, a] for a in agt_names]) == 1, \
            'Computation {} hosted'.format(c)

    # solve using GLPK
    status = pb.solve(solver=GLPK_CMD(keepFiles=1, msg=False,
                                      options=['--pcost']))

    if status != LpStatusOptimal:
        raise ImpossibleDistributionException("No possible optimal"
                                              " distribution ")
    logger.debug('GLPK cost : %s', value(pb.objective))

    # print('BETAS:')
    # for c1, a1, c2, a2 in betas:
    #     print('  ', c1, a1, c2, a2, value(betas[(c1, a1, c2, a2)]))
    #
    # print('XS:')
    # for c, a in xs:
    #     print('  ', c, a, value(xs[(c, a)]))

    mapping = {}
    for k in agt_names:
        agt_computations = [i for i, ka in xs
                            if ka == k and value(xs[(i, ka)]) == 1]
        # print(k, ' -> ', agt_computations)
        mapping[k] = agt_computations
    return mapping
Exemplo n.º 12
0
def distribute_factors(
    agents: Dict[str, AgentDef],
    cg: ComputationGraph,
    footprints: Dict[str, float],
    mapping: Dict[str, List[str]],
    msg_load: Callable[[str, str], float],
) -> Dict[str, List[str]]:
    """
    Optimal distribution of factors on agents.

    Parameters
    ----------
    cg: computations graph

    agents: dict
        a dict {agent_name : AgentDef} containing all available agents

    Returns
    -------
    a dict { agent_name: list of factor names}
    """
    pb = LpProblem("ilp_factors", LpMinimize)

    # build the inverse mapping var -> agt
    inverse_mapping = {}  # type: Dict[str, str]
    for a in mapping:
        inverse_mapping[mapping[a][0]] = a

    # One binary variable xij for each (variable, agent) couple
    factor_names = [n.name for n in cg.nodes if isinstance(n, FactorComputationNode)]
    xs = LpVariable.dict("x", (factor_names, agents), cat=LpBinary)
    logger.debug("Binary variables for factor distribution : %s", xs)

    # Hard constraints: respect agent's capacity
    for a in agents:
        # Footprint of the variable this agent is already hosting:
        v_footprint = footprints[mapping[a][0]]
        pb += (
            lpSum([footprints[fn] * xs[fn, a] for fn in factor_names])
            <= (agents[a].capacity - v_footprint),
            "Agent {} capacity".format(a),
        )

    # Hard constraints: all computations must be hosted.
    for c in factor_names:
        pb += lpSum([xs[c, a] for a in agents]) == 1, "Factor {} hosted".format(c)

    # 1st objective : minimize communication costs:
    comm = LpAffineExpression()
    for (fn, an_f) in xs:
        for vn in cg.neighbors(fn):
            an_v = inverse_mapping[vn]  # agt hosting neighbor var vn
            comm += agents[an_f].route(an_v) * msg_load(vn, fn) * xs[(fn, an_f)]

    # 2st objective : minimize hosting costs
    hosting = lpSum([agents[a].hosting_cost(c) * xs[(c, a)] for c, a in xs])

    # agregate the two objectives using RATIO_HOST_COMM
    pb += lpSum([RATIO_HOST_COMM * comm, (1 - RATIO_HOST_COMM) * hosting])

    # solve using GLPK and convert to mapping { agt_name : [factors names]}
    status = pb.solve(solver=GLPK_CMD(keepFiles=1, msg=False, options=["--pcost"]))
    if status != LpStatusOptimal:
        raise ImpossibleDistributionException(
            "No possible optimal distribution for factors"
        )
    logger.debug("GLPK cost : %s", value(pb.objective))
    mapping = {}  # type: Dict[str, List[str]]
    for k in agents:
        agt_computations = [i for i, ka in xs if ka == k and value(xs[(i, ka)]) == 1]
        # print(k, ' -> ', agt_computations)
        mapping[k] = agt_computations
    logger.debug("Factors distribution : %s ", mapping)
    return mapping
Exemplo n.º 13
0
Arquivo: aws.py Projeto: HEP-KBFI/ccc
def get_optimal_ec2(cpu_usage, mem_usage, optimal=True, debug=False):
    """ 
    Calculates the optimal allocation of resources over a certain time span (with monthly granularity). 
    Formulates the problem of satisfying user demand (in CPU and RAM) as an LP problem with a monetary objective function. 
    
    Returns allocation of reserved instances and a total price for running the allocation on AWS.    
    """    
    assert(len(cpu_usage) == len(mem_usage))
    
    prob = LpProblem("The Simplified EC2 cost optimization", LpMinimize)
    # variables
    ## 1h instances (both on-demand and reserved)
    per_h_ondems = []
    per_h_reserved = []
    for p in range(len(cpu_usage)):
        # ondemand
        per_h_ondems += ["p %s ondem %s" %(p, i) for i in vms.keys()]
        # reserved
        per_h_reserved += ["p %s reserved %s" %(p, i) for i in vms.keys()]
        
    ## nr of 1-year reserved instances
    nr_of_1year_reserved = [ "res_1year %s" % i for i in vms.keys()]
    nr_of_3year_reserved = [ "res_3year %s" % i for i in vms.keys()]
    
    category = LpInteger if optimal else LpContinuous
    vars = LpVariable.dicts("aws", per_h_ondems + per_h_reserved + nr_of_1year_reserved + nr_of_3year_reserved, \
                            lowBound = 0, upBound = None, cat = category)

    # objective function    
    prob += lpSum([vars[vm] * vms[vm.split(" ")[3]][0] for vm in per_h_ondems]) \
            + lpSum([vars[vm] * vms[vm.split(" ")[3]][3] for vm in per_h_reserved]) \
            + lpSum([vars[vm] * vms[vm.split(" ")[1]][1] for vm in nr_of_1year_reserved]) \
            + lpSum([vars[vm] * vms[vm.split(" ")[1]][2] for vm in nr_of_3year_reserved]) \
            , "Total cost of running the infrastructure consuming (CPU/RAM)/h" 

    # constraints
    ## demand constraints
    for p in range(len(cpu_usage)):
        prob += lpSum([vars[vm] * vms[vm.split(" ")[3]][4] for vm in (per_h_ondems + per_h_reserved) if int(vm.split(" ")[1]) == p]) >= cpu_usage[p], "CPU demand, period %s" %p
        prob += lpSum([vars[vm] * vms[vm.split(" ")[3]][5] for vm in (per_h_ondems + per_h_reserved) if int(vm.split(" ")[1]) == p]) >= mem_usage[p], "RAM demand. period %s" %p
    
    ## constraints on the reserved instances - cannot use more than we paid for
    for i in per_h_reserved:
        t = i.split(" ")[3]
        prob += vars["res_1year %s" % t] + vars["res_3year %s" % t] >= vars[i], "Nr. of used reserved machines of type %s" %i    
    prob.solve()   
    
    
    if debug:
        print "Status:", LpStatus[prob.status]
        print "Total Cost of the solution = ", value(prob.objective)
    
    res_instance = {}
    for v in prob.variables():
        if v.name.startswith("aws_res_") and v.varValue != 0.0:
            res_instance[v.name] = v.varValue
        
        if debug and v.varValue != 0.0:
            print v.name, "=", v.varValue
    
    return (res_instance, value(prob.objective))