Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0728c21
Nautilus - models and schemas for /navigate and /initialize.
PtrBednarik Feb 23, 2026
6696eac
Merge pull request #11 from PtrBednarik/dev/nautilus-navigator
PtrBednarik Feb 23, 2026
309c9bd
Merge branch 'nautilus-models'
PtrBednarik Feb 25, 2026
d2a96af
StateDB used, node_type in schemas removed, generic_states additions …
PtrBednarik Mar 4, 2026
50bfcd1
Merge branch 'industrial-optimization-group:master' into dev/nautilus…
PtrBednarik Mar 4, 2026
c5d0bc3
Merge branch 'industrial-optimization-group:master' into master
PtrBednarik Mar 4, 2026
654f27d
Repair, new version disabled.
PtrBednarik Mar 4, 2026
a0abc56
Merge pull request #12 from PtrBednarik/dev/nautilus-navigator
PtrBednarik Mar 4, 2026
ce1421e
Merge branch 'industrial-optimization-group:master' into master
PtrBednarik Mar 9, 2026
1085ee0
Two states - NautilusNavigatorInitializationState and NautilusNavigat…
PtrBednarik Mar 9, 2026
84ea731
Merge pull request #13 from PtrBednarik/dev/nautilus-navigator
PtrBednarik Mar 9, 2026
f0d815a
Removing useless parameters.
PtrBednarik Mar 11, 2026
cfb27de
Documentation fix.
PtrBednarik Mar 11, 2026
42c1624
Merge pull request #14 from PtrBednarik/dev/nautilus-navigator
PtrBednarik Mar 11, 2026
e56c969
Merge branch 'industrial-optimization-group:master' into master
PtrBednarik Mar 18, 2026
58ccf79
Fixed.
PtrBednarik Mar 18, 2026
3080c09
Merge pull request #15 from PtrBednarik/dev/nautilus-navigator
PtrBednarik Mar 18, 2026
e19507d
Merge branch 'industrial-optimization-group:master' into master
PtrBednarik Mar 20, 2026
692f22e
Merge branch 'industrial-optimization-group:master' into master
PtrBednarik Mar 23, 2026
d0347f8
Endpoint for getting HTTP exception based on error code.
PtrBednarik Mar 23, 2026
8c46caa
Merge pull request #16 from PtrBednarik/dev/error-code-endpoint
PtrBednarik Mar 23, 2026
9752a4a
nautilus-navigator endpoints
PtrBednarik Mar 23, 2026
7858301
Nautilus Navigator endpoints /initialize and /navigate + tests.v. 1.0
PtrBednarik Mar 26, 2026
33aafb0
Merge pull request #17 from PtrBednarik/dev/nautilus-navigator-endpoints
PtrBednarik Mar 26, 2026
aff2245
Merge branch 'master' into res-conflicts
PtrBednarik Mar 26, 2026
2c3fc11
Web-API
gialmisi Apr 8, 2026
e15bd0e
Web-API
gialmisi Apr 8, 2026
e3bf514
Merge branch 'master' of github.com:industrial-optimization-group/DES…
gialmisi Apr 8, 2026
e80a42e
Web-GUI
gialmisi Apr 8, 2026
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
2 changes: 2 additions & 0 deletions desdeo/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from desdeo.api.routers import (
enautilus,
generic,
nautilus_navigator,
nimbus,
problem,
reference_point_method,
Expand Down Expand Up @@ -41,6 +42,7 @@
app.include_router(enautilus.router)
app.include_router(site_selection.router)
app.include_router(gdm_score_bands_routers.router)
app.include_router(nautilus_navigator.router)


@app.get("/health")
Expand Down
3 changes: 3 additions & 0 deletions desdeo/api/db.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
"""Database configuration file for the API."""

from sqlalchemy import event
from sqlalchemy.orm import declarative_base
from sqlmodel import Session, create_engine

from desdeo.api.config import DatabaseConfig, SettingsConfig

Base = declarative_base()

if SettingsConfig.debug:
# debug and development stuff

Expand Down
16 changes: 16 additions & 0 deletions desdeo/api/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@
"ENautilusStepResponse",
"ENautilusTreeNodeResponse",
"ExtraFunctionDB",
"NautilusNavigatorInitializationState",
"NautilusNavigatorInitRequest",
"NautilusNavigatorInitResponse",
"NautilusNavigatorNavigateRequest",
"NautilusNavigatorNavigateResponse",
"NautilusNavigatorNavigationState",
"NautilusNavigatorStep",
"ForestProblemMetaData",
"GenericIntermediateSolutionResponse",
"GNIMBUSOptimizationState",
Expand Down Expand Up @@ -216,6 +223,15 @@
StateDB,
UserSavedSolutionDB,
)
from .nautilus_navigator import (
NautilusNavigatorInitializationState,
NautilusNavigatorInitRequest,
NautilusNavigatorInitResponse,
NautilusNavigatorNavigateRequest,
NautilusNavigatorNavigateResponse,
NautilusNavigatorNavigationState,
NautilusNavigatorStep,
)
from .nimbus import (
NIMBUSClassificationRequest,
NIMBUSClassificationResponse,
Expand Down
7 changes: 7 additions & 0 deletions desdeo/api/models/generic_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from desdeo.problem import Tensor, VariableType

from .nautilus_navigator import NautilusNavigatorInitializationState, NautilusNavigatorNavigationState
from .state import (
EMOFetchState,
EMOIterateState,
Expand Down Expand Up @@ -65,6 +66,8 @@ class StateKind(str, Enum):
GENERIC_INTERMEDIATE = "generic.solve_intermediate"
ENAUTILUS_STEP = "e-nautilus.stepping"
ENAUTILUS_FINAL = "e-nautilus.final"
NAUTILUS_NAVIGATE = "nautilus.navigate"
NAUTILUS_INITIALIZE = "nautilus.initialize"
XNIMBUS_SOLVE = "xnimbus.solve_candidates"
XNIMBUS_SAVE = "xnimbus.save_solutions"
XNIMBUS_INIT = "xnimbus.initialize"
Expand Down Expand Up @@ -199,6 +202,8 @@ def state(self) -> SQLModel | None:
StateKind.GENERIC_INTERMEDIATE: IntermediateSolutionState,
StateKind.ENAUTILUS_STEP: ENautilusState,
StateKind.ENAUTILUS_FINAL: ENautilusFinalState,
StateKind.NAUTILUS_NAVIGATE: NautilusNavigatorNavigationState,
StateKind.NAUTILUS_INITIALIZE: NautilusNavigatorInitializationState,
StateKind.XNIMBUS_SOLVE: NIMBUSClassificationState,
StateKind.XNIMBUS_SAVE: NIMBUSSaveState,
StateKind.XNIMBUS_INIT: NIMBUSInitializationState,
Expand All @@ -221,6 +226,8 @@ def state(self) -> SQLModel | None:
IntermediateSolutionState: StateKind.GENERIC_INTERMEDIATE,
ENautilusState: StateKind.ENAUTILUS_STEP,
ENautilusFinalState: StateKind.ENAUTILUS_FINAL,
NautilusNavigatorNavigationState: StateKind.NAUTILUS_NAVIGATE,
NautilusNavigatorInitializationState: StateKind.NAUTILUS_INITIALIZE,
}


Expand Down
142 changes: 142 additions & 0 deletions desdeo/api/models/nautilus_navigator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"""Models specific to the NAUTILUS Navigator method."""

from sqlalchemy import JSON, Column
from sqlmodel import Field, SQLModel


class NautilusNavigatorInitializationState(SQLModel, table=True):
"""State representing initialization of a NAUTILUS Navigator session.

This state corresponds to the execution of the `navigator_init` function
in the NAUTILUS Navigator algorithm.

The initialization currently requires no explicit parameters from the API
since the required information (problem and solver) is obtained from the
surrounding application context. Therefore, this state only stores the
primary key linking it to the base `State` entry.

Future versions may include additional fields such as
`non_dominated_solutions_id` if the algorithm later supports explicitly
supplying these.
"""

__tablename__ = "nautilus_navigator_initialization_states"

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


class NautilusNavigatorNavigationState(SQLModel, table=True):
"""State representing one execution of the NAUTILUS Navigator navigation step.

This state corresponds to a call to the `navigator_all_steps` function in
the NAUTILUS Navigator algorithm.

The design follows the standard pattern used in DESDEO method states:

- Fields correspond to the input arguments of the algorithm function
- A single field stores the result returned by the function

This allows the API to:
1. Retrieve previously computed navigation results without
re-running the algorithm.
2. Re-evaluate the algorithm if necessary, since the original
input parameters are stored.

Notes:
The parameters `problem` and `solver` are not stored in the state,
as they are provided by the surrounding application context.

Stored Inputs (arguments to `navigator_all_steps`):
steps_remaining:
Number of navigation steps to perform.

reference_point:
The reference point provided by the decision maker.

previous_responses:
The list of previous NAUTILUS responses representing the
navigation history up to this point.

bounds:
Optional bounds specified by the decision maker.

Stored Output:
navigator_results:
The list of responses returned by `navigator_all_steps`.
Each entry corresponds to one computed navigation step.
"""

__tablename__ = "nautilus_navigator_navigation_states"

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

steps_remaining: int
reference_point: dict[str, float] = Field(sa_column=Column(JSON))
bounds: dict[str, float] | None = Field(default=None, sa_column=Column(JSON))
previous_responses: list[dict] = Field(sa_column=Column(JSON))
navigator_results: list[dict] = Field(sa_column=Column(JSON))


class NautilusNavigatorInitRequest(SQLModel):
"""Request to initialize a NAUTILUS Navigator session."""

problem_id: int
session_id: int | None = Field(default=None)
parent_state_id: int | None = Field(default=None)


class NautilusNavigatorNavigateRequest(SQLModel):
"""Request to perform NAUTILUS Navigator navigation steps."""

problem_id: int
session_id: int | None = Field(default=None)
parent_state_id: int | None = Field(default=None)
reference_point: dict[str, float] = Field(
sa_column=Column(JSON),
description="Reference point provided by the decision maker.",
)
bounds: dict[str, float] | None = Field(
default=None,
sa_column=Column(JSON),
description="The bounds preference of the DM for each objective.",
)
steps_remaining: int = Field(description="The number of steps remaining in the navigation process.")


class NautilusNavigatorStep(SQLModel):
"""A single NAUTILUS Navigator step result."""

step_number: int
navigation_point: dict[str, float] = Field(sa_column=Column(JSON))
lower_bounds: dict[str, float] = Field(sa_column=Column(JSON))
upper_bounds: dict[str, float] = Field(sa_column=Column(JSON))
reachable_solution: dict[str, float] | None = Field(default=None, sa_column=Column(JSON))
reference_point: dict[str, float] | None = Field(default=None, sa_column=Column(JSON))
bounds: dict[str, float] | None = Field(default=None, sa_column=Column(JSON))
distance_to_front: float


class NautilusNavigatorInitResponse(SQLModel):
"""Response from NAUTILUS Navigator initialization."""

state_id: int | None = Field(description="The id of the state created by this initialization.")
navigation_point: dict[str, float] = Field(sa_column=Column(JSON), description="Initial navigation point.")
lower_bounds: dict[str, float] = Field(sa_column=Column(JSON), description="Lower bounds of reachable region.")
upper_bounds: dict[str, float] = Field(sa_column=Column(JSON), description="Upper bounds of reachable region.")
step_number: int = Field(description="Step number (always 0 at initialization).")
distance_to_front: float = Field(description="Distance to Pareto front.")


class NautilusNavigatorNavigateResponse(SQLModel):
"""Response from NAUTILUS Navigator navigation."""

state_id: int | None = Field(description="The id of the state created by this navigation step.")
steps: list[NautilusNavigatorStep] = Field(description="The computed navigation steps.")
32 changes: 32 additions & 0 deletions desdeo/api/routers/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,35 @@ def calculate_score_bands_from_objective_data(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Error calculating SCORE bands parameters: {e!r}",
) from e


@router.get("/debug/{httpcode}", tags=["debug"])
async def trigger_error(httpcode: int):
"""Debug endpoint to simulate HTTP errors.

This endpoint takes a 3-digit HTTP status code as a path parameter
and raises the corresponding HTTPException.

Example usage:
/method/generic/debug/404
/method/generic/debug/500

Args:
httpcode (int): A valid HTTP status code (100-599)

Raises:
HTTPException: Returns the HTTP error corresponding to `httpcode`.

Reference:
https://fastapi.tiangolo.com/tutorial/handling-errors/
"""
if httpcode < 100 or httpcode > 599: # noqa: PLR2004
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid HTTP code. Must be between 100 and 599.",
)

raise HTTPException(
status_code=httpcode,
detail=f"Debug triggered HTTP {httpcode} error",
)
Loading
Loading