Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 5 additions & 64 deletions desdeo/api/models/generic_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@

from desdeo.problem import Tensor, VariableType

from .nautilus import (
NautilusState,
)
from .nautilus import NautilusNavigatorInitializationState, NautilusNavigatorNavigationState
from .state import (
EMOFetchState,
EMOIterateState,
Expand Down Expand Up @@ -160,63 +158,6 @@ def create(

return row

# @classmethod
# def create(
# cls,
# database_session: Session,
# *,
# kind: StateKind,
# problem_id: int | None = None,
# session_id: int | None = None,
# parent_id: int | None = None,
# state: SQLModel | None = None,
# ) -> "StateDB":
# """Create a new StateDB entry and corresponding substate entry.

# Args:
# database_session: Database session
# kind: Explicit StateKind of this state.
# problem_id: Required for root states.
# session_id: Required for child states.
# parent_id: Parent state ID if this is a child.
# state: The substate SQLModel instance.
# """
# if state is None:
# raise ValueError("State (substate) must be provided.")

# if parent_id is None:
# # Root state
# if problem_id is None:
# raise ValueError("Root state requires problem_id.")
# state_db = cls(
# problem_id=problem_id,
# session_id=None,
# parent_id=None,
# kind=kind,
# )
# else:
# # Child state
# if session_id is None:
# raise ValueError("Child state requires session_id.")
# state_db = cls(
# problem_id=None,
# session_id=session_id,
# parent_id=parent_id,
# kind=kind,
# )

# database_session.add(state_db)
# database_session.commit()
# database_session.refresh(state_db)

# # Link substate
# state.state_id = state_db.id
# database_session.add(state)
# database_session.commit()
# database_session.refresh(state_db)

# return state_db

@property
def state(self) -> SQLModel | None:
"""Return the concrete substate instance (e.g., NIMBUSSaveState)...
Expand Down Expand Up @@ -256,8 +197,8 @@ def state(self) -> SQLModel | None:
StateKind.GENERIC_INTERMEDIATE: IntermediateSolutionState,
StateKind.ENAUTILUS_STEP: ENautilusState,
StateKind.ENAUTILUS_FINAL: ENautilusFinalState,
StateKind.NAUTILUS_NAVIGATE: NautilusState,
StateKind.NAUTILUS_INITIALIZE: NautilusState
StateKind.NAUTILUS_NAVIGATE: NautilusNavigatorNavigationState,
StateKind.NAUTILUS_INITIALIZE: NautilusNavigatorInitializationState
}

SUBSTATE_TO_KIND: dict[SQLModel, StateKind] = {
Expand All @@ -276,8 +217,8 @@ def state(self) -> SQLModel | None:
IntermediateSolutionState: StateKind.GENERIC_INTERMEDIATE,
ENautilusState: StateKind.ENAUTILUS_STEP,
ENautilusFinalState: StateKind.ENAUTILUS_FINAL,
# NautilusState: StateKind.NAUTILUS_NAVIGATE,
# NautilusState: StateKind.NAUTILUS_INITIALIZE
NautilusNavigatorNavigationState: StateKind.NAUTILUS_NAVIGATE,
NautilusNavigatorInitializationState: StateKind.NAUTILUS_INITIALIZE
}


Expand Down
119 changes: 88 additions & 31 deletions desdeo/api/models/nautilus.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,46 @@
from sqlmodel import JSON, Column, Field, SQLModel


class NautilusState(SQLModel, table=True):
"""Concrete NAUTILUS Navigator state stored for a single interaction step.
class NautilusNavigatorInitializationState(SQLModel, table=True):
"""State storing the inputs and outputs of the NAUTILUS Navigator initialization.

This model stores the full algorithmic state returned by the
NAUTILUS Navigator after either initialization or a navigation step.
This state corresponds to the execution of the `navigator_init` function in the
NAUTILUS Navigator core algorithm. It stores both the request provided by the
user and the resulting initialization information returned by the algorithm.

The instance is linked to a base `StateDB` entry, which defines the
problem context and state type (e.g. "nautilus.initialize" or
"nautilus.navigate"). This table contains only the algorithm-specific
data required to reconstruct the navigation process at that step.
The state is linked to a base `StateDB` entry which defines the interaction
type (`StateKind.NAUTILUS_INITIALIZE`) and stores the session hierarchy.

The purpose of storing this information is to allow the API to:
1. Retrieve previously computed initialization results without re-running
the algorithm.
2. Reconstruct the algorithm state if the function must be re-evaluated.

Attributes:
id (int | None): Primary key of this NAUTILUS state entry.
state_id (int): Foreign key referencing the base `StateDB` entry.

request (dict): Serialized request data passed to `navigator_init`.
response (dict): Serialized response returned by `navigator_init`.

objective_symbols (list[str]): Short symbolic names of the objectives.
objective_long_names (list[str]): Descriptive names of the objectives.
units (list[str] | None): Units of the objectives, if defined. None if unitless.
units (list[str] | None): Units of the objectives if defined, otherwise None.
is_maximized (list[bool]): Boolean flags indicating whether each objective
is to be maximized (True) or minimized (False).

ideal (list[float]): Ideal objective values of the problem.
nadir (list[float]): Nadir objective values of the problem.
lower_bounds (dict[str, list[float]]): Lower bounds of the reachable region per objective
across navigation steps.
upper_bounds (dict[str, list[float]]): Upper bounds of the reachable region per objective
across navigation steps.
preferences (dict[str, list[float]]): Preference values provided by the decision maker
for each navigation step.
bounds (dict[str, list[float]]): Bound preferences provided by the decision maker
for each navigation step.
total_steps (int): Total number of steps allowed in this NAUTILUS session.
current_step (int): Current navigation step index.
remaining_steps (int): Number of steps remaining in the navigation process.
reachable_solution (dict[str, float]): The objective values of the currently reachable solution.

total_steps (int): Total number of navigation steps specified for the session.
"""

__tablename__ = "nautilus_states"
__tablename__ = "nautilus_navigator_initialization_states"

id: int | None = Field(default=None, primary_key=True)
state_id: int = Field(foreign_key="state.id", primary_key=True)

# Stored request/response
request: dict = Field(sa_column=Column(JSON))
response: dict = Field(sa_column=Column(JSON))

# Problem meta
objective_symbols: list[str] = Field(sa_column=Column(JSON))
Expand All @@ -49,15 +52,69 @@ class NautilusState(SQLModel, table=True):
ideal: list[float] = Field(sa_column=Column(JSON))
nadir: list[float] = Field(sa_column=Column(JSON))

# Navigation data
lower_bounds: dict[str, list[float]] = Field(sa_column=Column(JSON))
upper_bounds: dict[str, list[float]] = Field(sa_column=Column(JSON))
preferences: dict[str, list[float]] = Field(sa_column=Column(JSON))
bounds: dict[str, list[float]] = Field(sa_column=Column(JSON))

# Navigator configuration
total_steps: int


class NautilusNavigatorNavigationState(SQLModel, table=True):
"""State storing the inputs and outputs of a NAUTILUS Navigator navigation step.

This state corresponds to the execution of the `navigator_all_steps` function
in the NAUTILUS Navigator algorithm. Each navigation step produces a new
reachable solution and updated bounds based on the decision maker's
preferences.

The state stores both the user input and the algorithm output so that:
1. The navigation history can be inspected without recomputing results.
2. The algorithm can be re-evaluated if needed.

The state is linked to a base `StateDB` entry which defines the interaction
type (`StateKind.NAUTILUS_NAVIGATE`) and the parent state relationship.

Attributes:
state_id (int): Foreign key referencing the base `StateDB` entry.

request (dict): Serialized navigation request provided by the decision maker.
response (dict): Serialized response returned by the navigator algorithm.

current_step (int): Current step index in the navigation process.
remaining_steps (int): Number of remaining navigation steps.

preferences (dict[str, list[float]]): Preference values provided by the
decision maker for each objective.

bounds (dict[str, list[float]]): Bound preferences provided by the
decision maker.

lower_bounds (dict[str, list[float]]): Lower bounds of the reachable
objective region after the navigation step.

upper_bounds (dict[str, list[float]]): Upper bounds of the reachable
objective region after the navigation step.

reachable_solution (dict[str, float]): Objective values of the currently
reachable solution produced by the navigation step.
"""

__tablename__ = "nautilus_navigator_navigation_states"

state_id: int = Field(foreign_key="state.id", primary_key=True)

# Stored request/response
request: dict = Field(sa_column=Column(JSON))
response: dict = Field(sa_column=Column(JSON))

# Navigation progress
current_step: int
remaining_steps: int

# Final reachable solution
# Decision maker input
preferences: dict[str, list[float]] = Field(sa_column=Column(JSON))
bounds: dict[str, list[float]] = Field(sa_column=Column(JSON))

# Reachable region bounds
lower_bounds: dict[str, list[float]] = Field(sa_column=Column(JSON))
upper_bounds: dict[str, list[float]] = Field(sa_column=Column(JSON))

# Resulting solution
reachable_solution: dict[str, float] = Field(sa_column=Column(JSON))
Loading