Skip to content

Commit

Permalink
refactor: restrucure and rename
Browse files Browse the repository at this point in the history
  • Loading branch information
joconnor-ecaa committed Dec 9, 2023
1 parent 34c26b7 commit 4d4c88a
Show file tree
Hide file tree
Showing 12 changed files with 1,742 additions and 383 deletions.
101 changes: 28 additions & 73 deletions examples/Christmas Dinner.ipynb

Large diffs are not rendered by default.

1,360 changes: 1,319 additions & 41 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pulp = ">=2.0.0"
pandas = ">=1.0.0"
pandas-stubs = ">=1.0.4.2"
numpy = ">=1.0.0"
pydantic = ">=2.0.0"

[tool.poetry.dev-dependencies]
Pygments = ">=2.10.0"
Expand All @@ -48,6 +49,8 @@ sphinx-click = ">=3.0.2"
typeguard = ">=2.13.3"
xdoctest = {extras = ["colors"], version = ">=0.15.10"}
myst-parser = {version = ">=0.16.1"}
ipykernel = "^6.27.1"
matplotlib = "^3.8.2"

[tool.poetry.scripts]
roastmaster = "roastmaster.__main__:main"
Expand Down Expand Up @@ -75,6 +78,7 @@ warn_unreachable = true
pretty = true
show_column_numbers = true
show_error_context = true
ignore_missing_imports = true

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
9 changes: 5 additions & 4 deletions src/roastmaster/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Roastmaster."""
from roastmaster.config import DishConfig
from roastmaster.config import SystemConfig
from roastmaster.dish import Dish
from roastmaster.optimiser import Optimiser
from roastmaster.models import Dish
from roastmaster.models import Hob
from roastmaster.models import Oven
from roastmaster.models import System
from roastmaster.session import Session
33 changes: 0 additions & 33 deletions src/roastmaster/config.py

This file was deleted.

72 changes: 31 additions & 41 deletions src/roastmaster/dish.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
"""Dish class for modelling a dish in the oven."""
"""dish.py."""

import pandas as pd
import pulp # type: ignore

from roastmaster import config
import roastmaster.models as models


class Dish:
class DishOpt:
"""Represents a dish that can be cooked in an oven.
Args:
model (pulp.LpProblem): The optimization model.
system_config (config.SystemConfig): The system configuration.
dish_config (config.DishConfig): The dish configuration.
system_config (models.System): The system configuration.
dish_config (models.Dish): The dish configuration.
Attributes:
name (str): The name of the dish.
size (float): The size of the dish.
serve_hot (float): The weight of the dish when served hot.
oven_mins (int): The desired cooking time in minutes.
system_config (config.SystemConfig): The system configuration.
system_config (models.System): The system configuration.
Methods:
get_score(): Calculates the score of the dish based on temperature and oven openings.
Expand All @@ -30,21 +30,21 @@ class Dish:
def __init__(
self,
model: pulp.LpProblem,
system_config: config.SystemConfig,
dish_config: config.DishConfig,
system_config: models.System,
dish_config: models.Dish,
) -> None:
"""Initializes a new instance of the Dish class.
Args:
model (pulp.LpProblem): The optimization model.
system_config (config.SystemConfig): The system configuration.
dish_config (config.DishConfig): The dish configuration.
system_config (models.System): The system configuration.
dish_config (models.Dish): The dish configuration.
"""
self.name = dish_config.name
self.size = dish_config.size
self.serve_hot = dish_config.serve_hot_weight
self.oven_mins = dish_config.oven_mins
self.dish_config = dish_config

self.system_config = system_config
self.time_range = system_config.get_time_range()

# initialise some dynamic decisions / variables at time = T-1
self.put_in: pulp.LpVariable = {-self.system_config.time_increment: 0}
Expand All @@ -53,12 +53,14 @@ def __init__(
self.space_used: pulp.LpVariable = {-self.system_config.time_increment: 0}
self.time_cooked: pulp.LpVariable = {-self.system_config.time_increment: 0}

for time in config.get_time_range(self.system_config):
for time in self.time_range:
# decision variables -- put into oven at this timestep
self.put_in[time] = pulp.LpVariable(f"{self.name}_in_{time}", cat="Binary")
self.put_in[time] = pulp.LpVariable(
f"{self.dish_config.name}_in_{time}", cat="Binary"
)
# take out of oven at this timestep
self.take_out[time] = pulp.LpVariable(
f"{self.name}_out_{time}", cat="Binary"
f"{self.dish_config.name}_out_{time}", cat="Binary"
)
# in-ness = last inness + put in - take out
self.is_in[time] = (
Expand All @@ -67,7 +69,7 @@ def __init__(
- self.take_out[time]
)
# space used = in-ness * size
self.space_used[time] = self.is_in[time] * self.size
self.space_used[time] = self.is_in[time] * self.dish_config.size
# time cooked = last time cooked + inness * time increment
self.time_cooked[time] = (
self.time_cooked[time - self.system_config.time_increment]
Expand All @@ -76,7 +78,7 @@ def __init__(
# penalty for multiple put-ins -- first e.g. 5 mins after
# putting in do not count towards cooking time
self.time_cooked[time] -= (
self.put_in[time] * self.system_config.warm_up_time
self.put_in[time] * self.system_config.oven.warm_up_time
)

# binary constraints on inness
Expand All @@ -94,7 +96,10 @@ def __init__(

# cooking time constraint -- total cooking time == desired cooking time
# TODO: add some tolerance here, maybe user-defined
model += self.time_cooked[self.system_config.total_time] == self.oven_mins
model += (
self.time_cooked[self.system_config.total_time]
== self.dish_config.cooking_time_mins
)
model += self.is_in[self.system_config.total_time] == 0

def get_score(self) -> pulp.LpAffineExpression:
Expand Down Expand Up @@ -124,8 +129,8 @@ def get_score(self) -> pulp.LpAffineExpression:
self.take_out[time] for time in self.take_out
)
return (
dish_temp * self.serve_hot
) - oven_openings * self.system_config.oven_opening_penalty
dish_temp * self.dish_config.serve_hot_weight
) - oven_openings * self.system_config.oven.oven_opening_penalty

def get_results(self) -> pd.DataFrame:
"""Retrieves the results of the optimization model.
Expand All @@ -135,34 +140,19 @@ def get_results(self) -> pd.DataFrame:
"""
is_in: pd.Series = pd.Series(
{
time: self.is_in[time].value()
for time in config.get_time_range(self.system_config)
}
{time: self.is_in[time].value() for time in self.time_range}
)
put_in: pd.Series = pd.Series(
{
time: self.put_in[time].value()
for time in config.get_time_range(self.system_config)
}
{time: self.put_in[time].value() for time in self.time_range}
)
take_out: pd.Series = pd.Series(
{
time: self.take_out[time].value()
for time in config.get_time_range(self.system_config)
}
{time: self.take_out[time].value() for time in self.time_range}
)
time_cooked: pd.Series = pd.Series(
{
time: self.time_cooked[time].value()
for time in config.get_time_range(self.system_config)
}
{time: self.time_cooked[time].value() for time in self.time_range}
)
space_used: pd.Series = pd.Series(
{
time: self.space_used[time].value()
for time in config.get_time_range(self.system_config)
}
{time: self.space_used[time].value() for time in self.time_range}
)
return pd.DataFrame(
{
Expand Down
168 changes: 168 additions & 0 deletions src/roastmaster/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
"""pydantic models."""
from datetime import time
from typing import Any

import numpy as np
from pydantic import BaseModel


class Dish(BaseModel):
"""Represents a dish with its name, size, cooking time etc.
Attributes:
name (str): The name of the dish.
cooking_time_mins (int): The cooking time of the dish in minutes.
resting_time_mins (int, optional): The resting time of the dish in minutes.
Defaults to 0.
size (float, optional): The size of the dish. Defaults to 0.5.
serve_hot_weight (float, optional): The weight of the dish when served hot.
Defaults to 1.
"""

name: str
cooking_time_mins: int
resting_time_mins: int = 0
size: float = 0.5
serve_hot_weight: float = 1

@classmethod
def get_preset(cls, name: str):
"""Get a preset dish configuration by name.
Args:
name (str): The name of the preset dish.
Returns:
Dish: The preset dish configuration.
"""
dish_presets = {
"nut_roast": cls(
name="nut_roast",
size=0.5,
serve_hot_weight=1,
cooking_time_mins=60,
resting_time_mins=10,
),
"turkey": cls(
name="turkey",
size=1.5,
serve_hot_weight=1,
cooking_time_mins=150,
resting_time_mins=30,
),
"chicken": cls(
name="chicken",
size=1,
serve_hot_weight=1,
cooking_time_mins=10,
resting_time_mins=15,
),
"roast_potatoes": cls(
name="roast_potatoes",
size=1,
serve_hot_weight=2.0,
cooking_time_mins=10,
),
"carrots": cls(
name="carrots",
size=0.5,
serve_hot_weight=1.0,
cooking_time_mins=25,
),
"parsnips": cls(
name="parsnips",
size=0.5,
serve_hot_weight=1.0,
cooking_time_mins=25,
),
"stuffing": cls(
name="stuffing",
size=0.5,
serve_hot_weight=1.0,
cooking_time_mins=25,
),
"pigs_in_blankets": cls(
name="pigs_in_blankets",
size=0.5,
serve_hot_weight=1.0,
cooking_time_mins=25,
),
"sprouts": cls(
name="sprouts",
size=0.5,
serve_hot_weight=1.0,
cooking_time_mins=25,
),
"yorkshire_puddings": cls(
name="yorkshire_puddings",
size=0.5,
serve_hot_weight=5.0,
cooking_time_mins=15,
),
}
return dish_presets[name]


class Oven(BaseModel):
"""Represents an oven used for roasting.
Attributes:
name (str): The name of the oven.
warm_up_time (float): The time it takes for the oven to warm up, in minutes.
Default is 10 minutes.
num_shelves (float): The number of shelves in the oven. Default is 2 shelves.
oven_opening_penalty (float): The penalty factor applied when the oven door is
opened during roasting. Default is 1.
"""

name: str
warm_up_time: float = 10
num_shelves: float = 2
oven_opening_penalty: float = 1


class Hob(BaseModel):
"""Represents a hob.
Attributes:
name (str): The name of the hob.
warm_up_time (float): The warm-up time of the hob in minutes. Default is 10.
num_rings (float): The number of rings on the hob. Default is 4.
"""

name: str
warm_up_time: float = 10
num_rings: float = 4


class System(BaseModel):
"""Represents a system configuration for roasting.
Attributes:
total_time (float): The total time for roasting.
time_increment (float, optional): The time increment for the time range
array. Defaults to 5.
dinner_time (time, optional): The dinner time. Defaults to 16:00.
oven (Oven): The oven object.
"""

total_time: float
time_increment: float = 5
dinner_time: time = time(16, 00)

oven: Oven

def get_time_range(self) -> np.ndarray[Any, Any]:
"""Generate a time range array based on the given system configuration.
Returns:
np.ndarray[Any, Any]: The time range array.
"""
return np.arange(0, self.total_time + 1, self.time_increment)


oven_presets = {"standard_oven": Oven(name="oven")}

hob_presets = {"standard_hob": Hob(name="hob")}
Loading

0 comments on commit 4d4c88a

Please sign in to comment.