Пример #1
0
def dietSolver(modelType):
    tdf = TicDatFactory(**dietSchema())
    addDietForeignKeys(tdf)
    addDietDataTypes(tdf)

    dat = tdf.copy_tic_dat(dietData())
    assert not tdf.find_data_type_failures(
        dat) and not tdf.find_foreign_key_failures(dat)

    mdl = Model(modelType, "diet")

    nutrition = {}
    for c, n in dat.categories.items():
        nutrition[c] = mdl.add_var(lb=n["minNutrition"],
                                   ub=n["maxNutrition"],
                                   name=c)

    # Create decision variables for the foods to buy
    buy = {}
    for f in dat.foods:
        buy[f] = mdl.add_var(name=f)

    # Nutrition constraints
    for c in dat.categories:
        mdl.add_constraint(mdl.sum(dat.nutritionQuantities[f, c]["qty"] *
                                   buy[f] for f in dat.foods) == nutrition[c],
                           name=c)

    mdl.set_objective(mdl.sum(buy[f] * c["cost"]
                              for f, c in dat.foods.items()))

    if mdl.optimize():
        solutionFactory = TicDatFactory(parameters=[[], ["totalCost"]],
                                        buyFood=[["food"], ["qty"]],
                                        consumeNutrition=[["category"],
                                                          ["qty"]])
        sln = solutionFactory.TicDat()
        for f, x in buy.items():
            if mdl.get_solution_value(x) > 0.0001:
                sln.buyFood[f] = mdl.get_solution_value(x)
        for c, x in nutrition.items():
            sln.consumeNutrition[c] = mdl.get_solution_value(x)
        return sln, sum(dat.foods[f]["cost"] * r["qty"]
                        for f, r in sln.buyFood.items())
Пример #2
0
def solve(dat):
    """
    core solving routine
    :param dat: a good ticdat for the input_schema
    :return: a good ticdat for the solution_schema, or None
    """
    assert input_schema.good_tic_dat_object(dat)
    assert not input_schema.find_foreign_key_failures(dat)
    assert not input_schema.find_data_type_failures(dat)
    assert not input_schema.find_data_row_failures(dat)

    mdl = Model(_model_type, "diet")

    nutrition = {
        c: mdl.add_var(lb=n["Min Nutrition"], ub=n["Max Nutrition"], name=c)
        for c, n in dat.categories.items()
    }

    # Create decision variables for the foods to buy
    buy = {f: mdl.add_var(name=f) for f in dat.foods}

    # Nutrition constraints
    for c in dat.categories:
        mdl.add_constraint(mdl.sum(dat.nutrition_quantities[f, c]["Quantity"] *
                                   buy[f] for f in dat.foods) == nutrition[c],
                           name=c)

    mdl.set_objective(mdl.sum(buy[f] * c["Cost"]
                              for f, c in dat.foods.items()))

    if mdl.optimize():
        sln = solution_schema.TicDat()
        for f, x in buy.items():
            if mdl.get_solution_value(x) > 0:
                sln.buy_food[f] = mdl.get_solution_value(x)
        for c, x in nutrition.items():
            sln.consume_nutrition[c] = mdl.get_solution_value(x)
        sln.parameters['Total Cost'] = sum(dat.foods[f]["Cost"] * r["Quantity"]
                                           for f, r in sln.buy_food.items())
        return sln
Пример #3
0
def solve(dat):
    """
    core solving routine
    :param dat: a good ticdat for the input_schema
    :return: a good ticdat for the solution_schema, or None
    """
    assert input_schema.good_tic_dat_object(dat)
    assert not input_schema.find_foreign_key_failures(dat)
    assert not input_schema.find_data_type_failures(dat)

    mdl = Model(_model_type, "netflow")

    flow = {(h, i, j): mdl.add_var(name='flow_%s_%s_%s' % (h, i, j))
            for h, i, j in dat.cost if (i, j) in dat.arcs}

    flowslice = Slicer(flow)

    # Arc Capacity constraints
    for i_, j_ in dat.arcs:
        mdl.add_constraint(mdl.sum(flow[h, i, j]
                                   for h, i, j in flowslice.slice('*', i_, j_))
                           <= dat.arcs[i_, j_]["Capacity"],
                           name='cap_%s_%s' % (i_, j_))

    # Flow conservation constraints. Constraints are generated only for relevant pairs.
    # So we generate a conservation of flow constraint if there is negative or positive inflow
    # quantity, or at least one inbound flow variable, or at least one outbound flow variable.
    for h,j in set(k for k,v in dat.inflow.items() if abs(v["Quantity"]) > 0)\
               .union({(h,i) for h,i,j in flow}, {(h,j) for h,i,j in flow}):
        mdl.add_constraint(
            mdl.sum(flow[h_, i_, j_]
                    for h_, i_, j_ in flowslice.slice(h, '*', j)) +
            dat.inflow.get((h, j), {"Quantity": 0})["Quantity"] == mdl.sum(
                flow[h_, j_, i_] for h_, j_, i_ in flowslice.slice(h, j, '*')),
            name='node_%s_%s' % (h, j))

    mdl.set_objective(
        mdl.sum(flow * dat.cost[h, i, j]["Cost"]
                for (h, i, j), flow in flow.items()))

    # Compute optimal solution
    if mdl.optimize():
        rtn = solution_schema.TicDat()
        for (h, i, j), var in flow.items():
            if mdl.get_solution_value(var) > 0:
                rtn.flow[h, i, j] = mdl.get_solution_value(var)
        rtn.parameters["Total Cost"] = sum(
            dat.cost[h, i, j]["Cost"] * r["Quantity"]
            for (h, i, j), r in rtn.flow.items())
        return rtn
Пример #4
0
def netflowSolver(modelType):
    tdf = TicDatFactory(**netflowSchema())
    addNetflowForeignKeys(tdf)
    addNetflowDataTypes(tdf)

    dat = tdf.copy_tic_dat(netflowData())
    assert not tdf.find_data_type_failures(
        dat) and not tdf.find_foreign_key_failures(dat)

    mdl = Model(modelType, "netflow")

    flow = {}
    for h, i, j in dat.cost:
        if (i, j) in dat.arcs:
            flow[h, i, j] = mdl.add_var(name='flow_%s_%s_%s' % (h, i, j))

    flowslice = Slicer(flow)

    for i_, j_ in dat.arcs:
        mdl.add_constraint(mdl.sum(flow[h, i, j]
                                   for h, i, j in flowslice.slice('*', i_, j_))
                           <= dat.arcs[i_, j_]["capacity"],
                           name='cap_%s_%s' % (i_, j_))

    for h_, j_ in set(k for k, v in dat.inflow.items()
                      if abs(v["quantity"]) > 0).union(
                          {(h, i)
                           for h, i, j in flow}, {(h, j)
                                                  for h, i, j in flow}):
        mdl.add_constraint(
            mdl.sum(flow[h, i, j]
                    for h, i, j in flowslice.slice(h_, '*', j_)) +
            dat.inflow.get((h_, j_), {"quantity": 0})["quantity"] == mdl.sum(
                flow[h, i, j] for h, i, j in flowslice.slice(h_, j_, '*')),
            name='node_%s_%s' % (h_, j_))

    mdl.set_objective(
        mdl.sum(flow * dat.cost[h, i, j]["cost"]
                for (h, i, j), flow in flow.items()))
    if mdl.optimize():
        solutionFactory = TicDatFactory(
            flow=[["commodity", "source", "destination"], ["quantity"]])
        if mdl.optimize():
            rtn = solutionFactory.TicDat()
            for (h, i, j), var in flow.items():
                if mdl.get_solution_value(var) > 0:
                    rtn.flow[h, i, j] = mdl.get_solution_value(var)
            return rtn, sum(dat.cost[h, i, j]["cost"] * r["quantity"]
                            for (h, i, j), r in rtn.flow.items())
Пример #5
0
def solve(dat):
    assert input_schema.good_tic_dat_object(dat)
    assert not input_schema.find_foreign_key_failures(dat)
    assert not input_schema.find_data_type_failures(dat)

    expected_draft_position = {}
    # for our purposes, its fine to assume all those drafted by someone else are drafted
    # prior to any players drafted by me
    for player_name in sorted(
            dat.players,
            key=lambda _p: {
                "Un-drafted": dat.players[_p]["Average Draft Position"],
                "Drafted By Me": -1,
                "Drafted By Someone Else": -2
            }[dat.players[_p]["Draft Status"]]):
        expected_draft_position[player_name] = len(expected_draft_position) + 1
    assert max(expected_draft_position.values()) == len(
        set(expected_draft_position.values())) == len(dat.players)
    assert min(expected_draft_position.values()) == 1

    already_drafted_by_me = {
        player_name
        for player_name, row in dat.players.items()
        if row["Draft Status"] == "Drafted By Me"
    }
    can_be_drafted_by_me = {
        player_name
        for player_name, row in dat.players.items()
        if row["Draft Status"] != "Drafted By Someone Else"
    }

    m = Model(_model_type, 'fantop')
    my_starters = {
        player_name: m.add_var(type="binary", name="starter_%s" % player_name)
        for player_name in can_be_drafted_by_me
    }
    my_reserves = {
        player_name: m.add_var(type="binary", name="reserve_%s" % player_name)
        for player_name in can_be_drafted_by_me
    }

    for player_name in can_be_drafted_by_me:
        if player_name in already_drafted_by_me:
            m.add_constraint(my_starters[player_name] +
                             my_reserves[player_name] == 1,
                             name="already_drafted_%s" % player_name)
        else:
            m.add_constraint(
                my_starters[player_name] + my_reserves[player_name] <= 1,
                name="cant_draft_twice_%s" % player_name)

    for i, draft_position in enumerate(sorted(dat.my_draft_positions)):
        m.add_constraint(m.sum(
            my_starters[player_name] + my_reserves[player_name]
            for player_name in can_be_drafted_by_me
            if expected_draft_position[player_name] < draft_position) <= i,
                         name="at_most_%s_can_be_ahead_of_%s" %
                         (i, draft_position))

    my_draft_size = m.sum(my_starters[player_name] + my_reserves[player_name]
                          for player_name in can_be_drafted_by_me)
    m.add_constraint(my_draft_size >= len(already_drafted_by_me) + 1,
                     name="need_to_extend_by_at_least_one")
    m.add_constraint(my_draft_size <= len(dat.my_draft_positions),
                     name="cant_exceed_draft_total")

    for position, row in dat.roster_requirements.items():
        players = {
            player_name
            for player_name in can_be_drafted_by_me
            if dat.players[player_name]["Position"] == position
        }
        starters = m.sum(my_starters[player_name] for player_name in players)
        reserves = m.sum(my_reserves[player_name] for player_name in players)
        m.add_constraint(starters >= row["Min Num Starters"],
                         name="min_starters_%s" % position)
        m.add_constraint(starters <= row["Max Num Starters"],
                         name="max_starters_%s" % position)
        m.add_constraint(reserves >= row["Min Num Reserve"],
                         name="min_reserve_%s" % position)
        m.add_constraint(reserves <= row["Max Num Reserve"],
                         name="max_reserve_%s" % position)

    if "Maximum Number of Flex Starters" in dat.parameters:
        players = {
            player_name
            for player_name in can_be_drafted_by_me
            if dat.roster_requirements[dat.players[player_name]["Position"]]
            ["Flex Status"] == "Flex Eligible"
        }
        m.add_constraint(
            m.sum(my_starters[player_name] for player_name in players) <=
            dat.parameters["Maximum Number of Flex Starters"]["Value"],
            name="max_flex")

    starter_weight = dat.parameters["Starter Weight"][
        "Value"] if "Starter Weight" in dat.parameters else 1
    reserve_weight = dat.parameters["Reserve Weight"][
        "Value"] if "Reserve Weight" in dat.parameters else 1
    m.set_objective(m.sum(dat.players[player_name]["Expected Points"] *
                          (my_starters[player_name] * starter_weight +
                           my_reserves[player_name] * reserve_weight)
                          for player_name in can_be_drafted_by_me),
                    sense="maximize")

    if not m.optimize():
        print("No draft at all is possible!")
        return

    sln = solution_schema.TicDat()

    def almostone(x):
        return abs(m.get_solution_value(x) - 1) < 0.0001

    picked = sorted([
        player_name for player_name in can_be_drafted_by_me
        if almostone(my_starters[player_name])
        or almostone(my_reserves[player_name])
    ],
                    key=lambda _p: expected_draft_position[_p])
    assert len(picked) <= len(dat.my_draft_positions)
    if len(picked) < len(dat.my_draft_positions):
        print(
            "Your model is over-constrained, and thus only a partial draft was possible"
        )

    draft_yield = 0
    for player_name, draft_position in zip(picked,
                                           sorted(dat.my_draft_positions)):
        draft_yield += dat.players[player_name]["Expected Points"] * \
                       (starter_weight if almostone(my_starters[player_name]) else reserve_weight)
        assert draft_position <= expected_draft_position[player_name]
        sln.my_draft[player_name]["Draft Position"] = draft_position
        sln.my_draft[player_name]["Position"] = dat.players[player_name][
            "Position"]
        sln.my_draft[player_name][
            "Planned Or Actual"] = "Actual" if player_name in already_drafted_by_me else "Planned"
        sln.my_draft[player_name]["Starter Or Reserve"] = \
            "Starter" if almostone(my_starters[player_name]) else "Reserve"
    sln.parameters["Total Yield"] = draft_yield
    sln.parameters["Feasible"] = len(sln.my_draft) == len(
        dat.my_draft_positions)

    return sln
Пример #6
0
def _testFantop(modelType, sqlFile):
    dataFactory = TicDatFactory(parameters=[["Key"], ["Value"]],
                                players=[['Player Name'],
                                         [
                                             'Position',
                                             'Average Draft Position',
                                             'Expected Points', 'Draft Status'
                                         ]],
                                roster_requirements=[['Position'],
                                                     [
                                                         'Min Num Starters',
                                                         'Max Num Starters',
                                                         'Min Num Reserve',
                                                         'Max Num Reserve',
                                                         'Flex Status'
                                                     ]],
                                my_draft_positions=[['Draft Position'], []])

    # add foreign key constraints (optional, but helps with preventing garbage-in, garbage-out)
    dataFactory.add_foreign_key("players", "roster_requirements",
                                ['Position', 'Position'])

    # set data types (optional, but helps with preventing garbage-in, garbage-out)
    dataFactory.set_data_type("parameters",
                              "Key",
                              number_allowed=False,
                              strings_allowed=[
                                  "Starter Weight", "Reserve Weight",
                                  "Maximum Number of Flex Starters"
                              ])
    dataFactory.set_data_type("parameters",
                              "Value",
                              min=0,
                              max=float("inf"),
                              inclusive_min=True,
                              inclusive_max=False)
    dataFactory.set_data_type("players",
                              "Average Draft Position",
                              min=0,
                              max=float("inf"),
                              inclusive_min=False,
                              inclusive_max=False)
    dataFactory.set_data_type("players",
                              "Expected Points",
                              min=-float("inf"),
                              max=float("inf"),
                              inclusive_min=False,
                              inclusive_max=False)
    dataFactory.set_data_type("players",
                              "Draft Status",
                              strings_allowed=[
                                  "Un-drafted", "Drafted By Me",
                                  "Drafted By Someone Else"
                              ])
    for fld in ("Min Num Starters", "Min Num Reserve", "Max Num Reserve"):
        dataFactory.set_data_type("roster_requirements",
                                  fld,
                                  min=0,
                                  max=float("inf"),
                                  inclusive_min=True,
                                  inclusive_max=False,
                                  must_be_int=True)
    dataFactory.set_data_type("roster_requirements",
                              "Max Num Starters",
                              min=0,
                              max=float("inf"),
                              inclusive_min=False,
                              inclusive_max=True,
                              must_be_int=True)
    dataFactory.set_data_type(
        "roster_requirements",
        "Flex Status",
        number_allowed=False,
        strings_allowed=["Flex Eligible", "Flex Ineligible"])
    dataFactory.set_data_type("my_draft_positions",
                              "Draft Position",
                              min=0,
                              max=float("inf"),
                              inclusive_min=False,
                              inclusive_max=False,
                              must_be_int=True)

    solutionFactory = TicDatFactory(my_draft=[[
        'Player Name'
    ], [
        'Draft Position', 'Position', 'Planned Or Actual', 'Starter Or Reserve'
    ]])

    dat = dataFactory.sql.create_tic_dat_from_sql(os.path.join(
        _codeDir(), sqlFile),
                                                  freeze_it=True)

    assert dataFactory.good_tic_dat_object(dat)
    assert not dataFactory.find_foreign_key_failures(dat)
    assert not dataFactory.find_data_type_failures(dat)

    expected_draft_position = {}
    # for our purposes, its fine to assume all those drafted by someone else are drafted
    # prior to any players drafted by me
    for player_name in sorted(
            dat.players,
            key=lambda _p: {
                "Un-drafted": dat.players[_p]["Average Draft Position"],
                "Drafted By Me": -1,
                "Drafted By Someone Else": -2
            }[dat.players[_p]["Draft Status"]]):
        expected_draft_position[player_name] = len(expected_draft_position) + 1
    assert max(expected_draft_position.values()) == len(
        set(expected_draft_position.values())) == len(dat.players)
    assert min(expected_draft_position.values()) == 1

    already_drafted_by_me = {
        player_name
        for player_name, row in dat.players.items()
        if row["Draft Status"] == "Drafted By Me"
    }
    can_be_drafted_by_me = {
        player_name
        for player_name, row in dat.players.items()
        if row["Draft Status"] != "Drafted By Someone Else"
    }

    m = Model(modelType, 'fantop')
    my_starters = {
        player_name: m.add_var(type="binary", name="starter_%s" % player_name)
        for player_name in can_be_drafted_by_me
    }
    my_reserves = {
        player_name: m.add_var(type="binary", name="reserve_%s" % player_name)
        for player_name in can_be_drafted_by_me
    }

    for player_name in can_be_drafted_by_me:
        if player_name in already_drafted_by_me:
            m.add_constraint(my_starters[player_name] +
                             my_reserves[player_name] == 1,
                             name="already_drafted_%s" % player_name)
        else:
            m.add_constraint(
                my_starters[player_name] + my_reserves[player_name] <= 1,
                name="cant_draft_twice_%s" % player_name)

    for i, draft_position in enumerate(sorted(dat.my_draft_positions)):
        m.add_constraint(m.sum(
            my_starters[player_name] + my_reserves[player_name]
            for player_name in can_be_drafted_by_me
            if expected_draft_position[player_name] < draft_position) <= i,
                         name="at_most_%s_can_be_ahead_of_%s" %
                         (i, draft_position))

    my_draft_size = m.sum(my_starters[player_name] + my_reserves[player_name]
                          for player_name in can_be_drafted_by_me)
    m.add_constraint(my_draft_size >= len(already_drafted_by_me) + 1,
                     name="need_to_extend_by_at_least_one")
    m.add_constraint(my_draft_size <= len(dat.my_draft_positions),
                     name="cant_exceed_draft_total")

    for position, row in dat.roster_requirements.items():
        players = {
            player_name
            for player_name in can_be_drafted_by_me
            if dat.players[player_name]["Position"] == position
        }
        starters = m.sum(my_starters[player_name] for player_name in players)
        reserves = m.sum(my_reserves[player_name] for player_name in players)
        m.add_constraint(starters >= row["Min Num Starters"],
                         name="min_starters_%s" % position)
        m.add_constraint(starters <= row["Max Num Starters"],
                         name="max_starters_%s" % position)
        m.add_constraint(reserves >= row["Min Num Reserve"],
                         name="min_reserve_%s" % position)
        m.add_constraint(reserves <= row["Max Num Reserve"],
                         name="max_reserve_%s" % position)

    if "Maximum Number of Flex Starters" in dat.parameters:
        players = {
            player_name
            for player_name in can_be_drafted_by_me
            if dat.roster_requirements[dat.players[player_name]["Position"]]
            ["Flex Status"] == "Flex Eligible"
        }
        m.add_constraint(
            m.sum(my_starters[player_name] for player_name in players) <=
            dat.parameters["Maximum Number of Flex Starters"]["Value"],
            name="max_flex")

    starter_weight = dat.parameters["Starter Weight"][
        "Value"] if "Starter Weight" in dat.parameters else 1
    reserve_weight = dat.parameters["Reserve Weight"][
        "Value"] if "Reserve Weight" in dat.parameters else 1
    m.set_objective(m.sum(dat.players[player_name]["Expected Points"] *
                          (my_starters[player_name] * starter_weight +
                           my_reserves[player_name] * reserve_weight)
                          for player_name in can_be_drafted_by_me),
                    sense="maximize")

    if not m.optimize():
        return

    sln = solutionFactory.TicDat()

    def almostone(x):
        return abs(m.get_solution_value(x) - 1) < 0.0001

    picked = sorted([
        player_name for player_name in can_be_drafted_by_me
        if almostone(my_starters[player_name])
        or almostone(my_reserves[player_name])
    ],
                    key=lambda _p: expected_draft_position[_p])
    assert len(picked) <= len(dat.my_draft_positions)
    if len(picked) < len(dat.my_draft_positions):
        print(
            "Your model is over-constrained, and thus only a partial draft was possible"
        )

    draft_yield = 0
    for player_name, draft_position in zip(picked,
                                           sorted(dat.my_draft_positions)):
        draft_yield += dat.players[player_name]["Expected Points"] * \
                       (starter_weight if almostone(my_starters[player_name]) else reserve_weight)
        assert draft_position <= expected_draft_position[player_name]
        sln.my_draft[player_name]["Draft Position"] = draft_position
        sln.my_draft[player_name]["Position"] = dat.players[player_name][
            "Position"]
        sln.my_draft[player_name][
            "Planned Or Actual"] = "Actual" if player_name in already_drafted_by_me else "Planned"
        sln.my_draft[player_name]["Starter Or Reserve"] = \
            "Starter" if almostone(my_starters[player_name]) else "Reserve"
    return sln, draft_yield
Пример #7
0
def solve(dat, progress):
    assert isinstance(progress, Progress)
    assert input_schema.good_tic_dat_object(dat)
    assert not input_schema.find_foreign_key_failures(dat)
    assert not input_schema.find_data_type_failures(dat)

    full_parameters = input_schema.create_full_parameters_dict(dat)

    def get_distance(x, y):
        if (x, y) in dat.distance:
            return dat.distance[x, y]["Distance"]
        if (y, x) in dat.distance:
            return dat.distance[y, x]["Distance"]
        return float("inf")

    def can_assign(x, y):
        return dat.sites[y]["Center Status"] == "Can Be Center" \
               and get_distance(x,y)<float("inf")

    m = Model(model_name="cog", model_type=full_parameters["Core Model Type"])

    assign_vars = {(n, assigned_to): m.add_var(type="binary",
                                               name="%s_%s" % (n, assigned_to))
                   for n in dat.sites for assigned_to in dat.sites
                   if can_assign(n, assigned_to)}
    open_vars = {
        n: m.add_var(type="binary", name="open_%s" % n)
        for n in dat.sites if dat.sites[n]["Center Status"] == "Can Be Center"
    }
    assert open_vars, "nothing can be center"

    assign_slicer = Slicer(assign_vars)

    for n, r in dat.sites.items():
        if r["Demand"] > 0:
            m.add_constraint(m.sum(
                assign_vars[n, assign_to]
                for _, assign_to in assign_slicer.slice(n, "*")) == 1,
                             name="must_assign_%s" % n)

    for assigned_to, r in dat.sites.items():
        if r["Center Status"] == "Can Be Center":
            _assign_vars = [
                assign_vars[n, assigned_to]
                for n, _ in assign_slicer.slice("*", assigned_to)
            ]
            # this is the weak formulation
            m.add_constraint(m.sum(_assign_vars) <=
                             len(_assign_vars) * open_vars[assigned_to],
                             name="weak_force_open%s" % assigned_to)

    number_of_centroids = full_parameters["Number of Centroids"]

    m.add_constraint(m.sum(v
                           for v in open_vars.values()) == number_of_centroids,
                     name="numCentroids")

    m.set_parameters(MIP_Gap=full_parameters["MIP Gap"])

    m.set_objective(m.sum(var * get_distance(n, assigned_to) *
                          dat.sites[n]["Demand"]
                          for (n, assigned_to), var in assign_vars.items()),
                    sense="minimize")
    if full_parameters["Core Model Type"] == "cplex":
        progress.add_cplex_listener("COG Optimization", m.core_model)

    worked = m.optimize(*(
        [progress.gurobi_call_back_factory("COG Optimization", m.core_model)]
        if full_parameters["Core Model Type"] == "gurobi" else []))

    assert worked, "testing model set up only for success"

    sln = solution_schema.TicDat()
    if full_parameters["Core Model Type"] == "gurobi":
        sln.parameters["Lower Bound"] = getattr(m.core_model, "objBound",
                                                m.core_model.objVal)
        sln.parameters["Upper Bound"] = m.core_model.objVal
    else:
        lb = m.core_model.solve_details.best_bound
        sln.parameters["Lower Bound"] = worked.get_objective_value() if isnan(
            lb) else lb
        sln.parameters["Upper Bound"] = worked.get_objective_value()

    def almostone(x):
        return abs(x - 1) < 0.0001

    for (n, assigned_to), var in assign_vars.items():
        if almostone(m.get_solution_value(var)):
            sln.assignments[n, assigned_to] = {}
    for n, var in open_vars.items():
        if almostone(m.get_solution_value(var)):
            sln.openings[n] = {}
    return sln