diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 2401f3f0..0ec82fe8 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -40,7 +40,7 @@ from .artifact._backend import register_artifact_route from .artifact._backend_to_store import to_artifact_store from .preferential._study import _SYSTEM_ATTR_PREFERENTIAL_STUDY from .preferential._study import get_best_trials as get_best_preferential_trials -from .preferential._system_attrs import report_preferences +from .preferential._system_attrs import report_preferences, report_skip if typing.TYPE_CHECKING: @@ -331,6 +331,23 @@ def create_app( response.status = 204 return {} + @app.post("/api/studies///skip") + @json_api_view + def skip_trial(study_id: int, trial_id: int) -> dict[str, Any]: + summary = get_study_summary(storage, study_id) + if summary is None: + response.status = 404 # Not found + return {"reason": f"study_id={study_id} is not found"} + system_attrs = getattr(summary, "system_attrs", {}) + is_preferential = system_attrs.get(_SYSTEM_ATTR_PREFERENTIAL_STUDY, False) + if not is_preferential: + response.status = 400 # Bad request + return {"reason": "The study is not preferential."} + + report_skip(study_id, trial_id, storage) + response.status = 204 # No content + return {} + @app.put("/api/studies///note") @json_api_view def save_trial_note(study_id: int, trial_id: int) -> dict[str, Any]: diff --git a/optuna_dashboard/preferential/_study.py b/optuna_dashboard/preferential/_study.py index 271eabfd..d74fbf14 100644 --- a/optuna_dashboard/preferential/_study.py +++ b/optuna_dashboard/preferential/_study.py @@ -12,8 +12,11 @@ from optuna.samplers import BaseSampler from optuna.samplers import RandomSampler from optuna.trial import FrozenTrial from optuna.trial import TrialState -from optuna_dashboard.preferential._system_attrs import get_preferences -from optuna_dashboard.preferential._system_attrs import report_preferences +from optuna_dashboard.preferential._system_attrs import ( + get_preferences, + report_preferences, + _SYSTEM_ATTR_SKIP_TRIAL, +) _logger = logging.get_logger(__name__) @@ -265,7 +268,12 @@ def get_best_trials(study_id: int, storage: optuna.storages.BaseStorage) -> list ] preferences = get_preferences(study_id, storage) worse_numbers = {worse for _, worse in preferences} - return [copy.deepcopy(t) for t in ready_trials if t.number not in worse_numbers] + skiped_numbers = storage.get_study_system_attrs(study_id).get(_SYSTEM_ATTR_SKIP_TRIAL, []) + return [ + copy.deepcopy(t) + for t in ready_trials + if t.number not in worse_numbers and t.number not in skiped_numbers + ] def create_study( diff --git a/optuna_dashboard/preferential/_system_attrs.py b/optuna_dashboard/preferential/_system_attrs.py index fdd9db35..0c2c8777 100644 --- a/optuna_dashboard/preferential/_system_attrs.py +++ b/optuna_dashboard/preferential/_system_attrs.py @@ -9,6 +9,7 @@ from .._storage import get_study_summary _SYSTEM_ATTR_PREFIX_PREFERENCE = "preference:values" +_SYSTEM_ATTR_SKIP_TRIAL = "preference:skip_trial" def report_preferences( @@ -44,3 +45,19 @@ def get_preferences( continue preferences.extend(v) # type: ignore return preferences + + +def report_skip( + study_id: int, + trial_id: int, + storage: BaseStorage, +): + system_attrs = storage.get_study_system_attrs(study_id) + trial_skiped: list[int] = system_attrs.get(_SYSTEM_ATTR_SKIP_TRIAL, []) + trial_number = storage.get_trial(trial_id).number + trial_skiped.append(trial_number) + storage.set_study_system_attr( + study_id=study_id, + key=_SYSTEM_ATTR_SKIP_TRIAL, + value=trial_skiped, + ) diff --git a/optuna_dashboard/ts/action.ts b/optuna_dashboard/ts/action.ts index c0fed1aa..017751fa 100644 --- a/optuna_dashboard/ts/action.ts +++ b/optuna_dashboard/ts/action.ts @@ -15,6 +15,7 @@ import { getMetaInfoAPI, deleteArtifactAPI, reportPreferenceAPI, + skipPreferentialTrialAPI, } from "./apiClient" import { graphVisibilityState, @@ -598,6 +599,16 @@ export const actionCreator = () => { }) } + const skipPreferentialTrial = (studyId: number, trialId: number) => { + skipPreferentialTrialAPI(studyId, trialId).catch((err) => { + const reason = err.response?.data.reason + enqueueSnackbar(`Failed to skip trial. Reason: ${reason}`, { + variant: "error", + }) + console.log(err) + }) + } + return { updateAPIMeta, updateStudyDetail, @@ -618,6 +629,7 @@ export const actionCreator = () => { makeTrialFail, saveTrialUserAttrs, updatePreference, + skipPreferentialTrial, } } diff --git a/optuna_dashboard/ts/apiClient.ts b/optuna_dashboard/ts/apiClient.ts index 5068dc3e..25ca3541 100644 --- a/optuna_dashboard/ts/apiClient.ts +++ b/optuna_dashboard/ts/apiClient.ts @@ -326,3 +326,14 @@ export const reportPreferenceAPI = ( return }) } + +export const skipPreferentialTrialAPI = ( + studyId: number, + trialId: number +): Promise => { + return axiosInstance + .post(`/api/studies/${studyId}/${trialId}/skip`) + .then(() => { + return + }) +} diff --git a/optuna_dashboard/ts/components/PreferentialTrials.tsx b/optuna_dashboard/ts/components/PreferentialTrials.tsx index 9b57586d..c6e7eeb0 100644 --- a/optuna_dashboard/ts/components/PreferentialTrials.tsx +++ b/optuna_dashboard/ts/components/PreferentialTrials.tsx @@ -28,6 +28,15 @@ const PreferentialTrial: FC<{ > Trial {trial.number} (trial_id={trial.trial_id}) +