Spaces:
Sleeping
Sleeping
| from datetime import datetime | |
| import numpy as np | |
| from numpy.typing import NDArray | |
| from pyscipopt import Model, quicksum | |
| from ui.display import format_results | |
| from data_loader import ( | |
| DISHES_DF, | |
| LABELS, | |
| PLANTS_DF, | |
| ) | |
| def optimize(budget, strategy, stocks, sold_prices): | |
| """ | |
| Calculate the optimal solution of item sales based on the given budget | |
| and inventory constraints. | |
| Args: | |
| - budget (int): The total budget available for purchasing items. | |
| - strategy (str): The strategy to use for optimization, either "MinimizeStock" or "MaximizeStock". | |
| - stocks (NDArray[int]): An array representing the available stock of each item. | |
| - sold_prices (NDArray[int]): An array representing the selling price of each item. | |
| Returns: | |
| - dict: A dictionary containing the solution, total price, total count, | |
| """ | |
| # Initialize the master problem | |
| model = Model("Knapsack") | |
| # Decision variables in master problem | |
| x = [ | |
| model.addVar( | |
| vtype="I", name=f"x_{i}", lb=0, ub=int(stocks[i]) if stocks[i] else 0 | |
| ) | |
| for i in range(len(stocks)) | |
| ] | |
| obj1 = quicksum(sold_prices[i] * x[i] for i in range(len(stocks))) | |
| obj2 = quicksum(x[i] for i in range(len(stocks))) | |
| # Objective: maximize total value of sold plants | |
| model.setObjective(obj1, "maximize") | |
| model.addCons(obj1 <= budget) | |
| # first optimize | |
| model.hideOutput() | |
| model.optimize() | |
| if model.getStatus() == "optimal": | |
| optimal_total_value = model.getObjVal() | |
| model.freeTransform() | |
| model.setObjective( | |
| obj2, "maximize" if strategy == "MinimizeStock" else "minimize" | |
| ) | |
| model.addCons(obj1 == optimal_total_value) | |
| model.optimize() | |
| # Final solution processing | |
| solution = [0] * len(x) | |
| total_value = 0 | |
| total_count = 0 | |
| if model.getStatus() == "optimal": | |
| for i, var in enumerate(x): | |
| if (n := round(model.getVal(var))) > 0 and sold_prices[i] > 0: | |
| solution[i] = n | |
| total_count += n | |
| total_value += n * sold_prices[i] | |
| return { | |
| "solution": solution, | |
| "total_price": int(total_value), | |
| "total_count": int(total_count), | |
| "remaining": int(budget - total_value), | |
| } | |
| raise ValueError( | |
| f"Optimization failed with status: {model.getStatus()} at {datetime.now()}" | |
| ) | |
| def get_results( | |
| language, | |
| currency, | |
| budget, | |
| plants_prices_extra_rate, | |
| dishes_prices_extra_rate, | |
| talent_price_bonus, | |
| strategy, | |
| *inventory, | |
| ): | |
| talent_price_bonus = talent_price_bonus or 0 | |
| prices: NDArray[np.int16] = np.concat( | |
| [ | |
| PLANTS_DF[currency] * (1 + plants_prices_extra_rate), | |
| np.floor( | |
| DISHES_DF[currency] | |
| * (1 + dishes_prices_extra_rate) | |
| * (1 + talent_price_bonus / 100) | |
| ).astype( | |
| np.int16 | |
| ), # Licet(@discord)'s data shows that all values are rounded down: https://docs.google.com/spreadsheets/d/1CWv0VmgfKKWWlqUty9hqGwWt86_G94x5K89DP4b4eRI | |
| ], | |
| dtype=np.int16, | |
| ) | |
| outputs = optimize( | |
| budget, | |
| strategy, | |
| np.array([n if n else 0 for n in inventory], dtype=np.int16), | |
| prices, | |
| ) | |
| plants_solution = outputs["solution"][: len(PLANTS_DF)] | |
| dishes_solution = outputs["solution"][ | |
| len(PLANTS_DF) : len(PLANTS_DF) + len(DISHES_DF) | |
| ] | |
| results = {} | |
| results["solution"] = { | |
| f"{LABELS[language]['plants'][PLANTS_DF.iloc[i]['name']]} ({LABELS[language]['tiers'][PLANTS_DF.iloc[i]['tier']]}, {int(prices[i])} {currency})": plants_solution[ | |
| i | |
| ] | |
| for i in range(len(PLANTS_DF)) | |
| if plants_solution[i] > 0 | |
| } | |
| results["solution"].update({ | |
| f"{LABELS[language]['dishes'][DISHES_DF.iloc[i]['name']]} ({LABELS[language]['tiers'][DISHES_DF.iloc[i]['tier']]}, {int(prices[len(PLANTS_DF) + i])} {currency})": dishes_solution[ | |
| i | |
| ] | |
| for i in range(len(DISHES_DF)) | |
| if dishes_solution[i] > 0 | |
| }) | |
| results["total_price"] = outputs["total_price"] | |
| results["total_count"] = outputs["total_count"] | |
| results["remaining"] = outputs["remaining"] | |
| return format_results(results, language) | |