From 52c090a9576e988737d702da15573b7b2a51050a Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Tue, 12 Dec 2023 13:26:44 +0100 Subject: [PATCH 1/8] Make running trials in timeline plot visible --- .../ts/components/GraphTimeline.tsx | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphTimeline.tsx b/optuna_dashboard/ts/components/GraphTimeline.tsx index cc0f7f57..29449a9d 100644 --- a/optuna_dashboard/ts/components/GraphTimeline.tsx +++ b/optuna_dashboard/ts/components/GraphTimeline.tsx @@ -65,10 +65,30 @@ const plotTimeline = (trials: Trial[], mode: string) => { ) ) ) - const maxDatetime = new Date( + const maxRunDuration = Math.max( + ...lastTrials.map( + (t) => { + const start = t.datetime_start?.getTime() ?? new Date().getTime() + const complete = t.datetime_complete?.getTime() ?? start + return complete - start + } + ) + ) + const hasRunning = maxRunDuration === 0 || lastTrials.some((t) => { + const isRunning = t.state === "Running" + if (!isRunning){ + return false + } + const start = t.datetime_start?.getTime() ?? new Date().getTime() + const now = new Date().getTime() + // This is an ad-hoc handling to check if the trial is running. + return now - start < maxRunDuration * 5 + } + ) + const maxDatetime = hasRunning ? new Date() : new Date( Math.max( ...lastTrials.map( - (t) => t.datetime_start?.getTime() ?? minDatetime.getTime() + (t) => t.datetime_complete?.getTime() ?? minDatetime.getTime() ) ) ) @@ -95,9 +115,16 @@ const plotTimeline = (trials: Trial[], mode: string) => { const makeTrace = (bars: Trial[], state: string, color: string) => { const starts = bars.map((b) => b.datetime_start ?? new Date()) const completes = bars.map((b, i) => b.datetime_complete ?? starts[i]) + const isRunning = state === "Running" + const runDurations = bars.map((b) => { + const start = b.datetime_start?.getTime() ?? new Date().getTime() + const complete = b.datetime_complete?.getTime() ?? start + return !isRunning ? complete - start : maxDatetime.getTime() - start + }) + starts.map((s, i) => Math.max(10, completes[i].getTime() - s.getTime())) const trace: Partial = { type: "bar", - x: starts.map((s, i) => completes[i].getTime() - s.getTime()), + x: runDurations, y: bars.map((b) => b.number), // @ts-ignore: To suppress ts(2322) base: starts.map((s) => s.toISOString()), From 059f4751acc17387eabd723f5caa547b29be63d2 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Tue, 12 Dec 2023 13:32:42 +0100 Subject: [PATCH 2/8] Apply formatter --- .../ts/components/GraphTimeline.tsx | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphTimeline.tsx b/optuna_dashboard/ts/components/GraphTimeline.tsx index 29449a9d..dfb97bc6 100644 --- a/optuna_dashboard/ts/components/GraphTimeline.tsx +++ b/optuna_dashboard/ts/components/GraphTimeline.tsx @@ -66,32 +66,33 @@ const plotTimeline = (trials: Trial[], mode: string) => { ) ) const maxRunDuration = Math.max( - ...lastTrials.map( - (t) => { - const start = t.datetime_start?.getTime() ?? new Date().getTime() - const complete = t.datetime_complete?.getTime() ?? start - return complete - start - } - ) + ...lastTrials.map((t) => { + const start = t.datetime_start?.getTime() ?? new Date().getTime() + const complete = t.datetime_complete?.getTime() ?? start + return complete - start + }) ) - const hasRunning = maxRunDuration === 0 || lastTrials.some((t) => { + const hasRunning = + maxRunDuration === 0 || + lastTrials.some((t) => { const isRunning = t.state === "Running" - if (!isRunning){ + if (!isRunning) { return false } const start = t.datetime_start?.getTime() ?? new Date().getTime() const now = new Date().getTime() // This is an ad-hoc handling to check if the trial is running. return now - start < maxRunDuration * 5 - } - ) - const maxDatetime = hasRunning ? new Date() : new Date( - Math.max( - ...lastTrials.map( - (t) => t.datetime_complete?.getTime() ?? minDatetime.getTime() + }) + const maxDatetime = hasRunning + ? new Date() + : new Date( + Math.max( + ...lastTrials.map( + (t) => t.datetime_complete?.getTime() ?? minDatetime.getTime() + ) + ) ) - ) - ) const layout: Partial = { margin: { l: 50, From 656994f8599d5b8f1d9c6f230454b9cfaec6d251 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Tue, 12 Dec 2023 14:19:36 +0100 Subject: [PATCH 3/8] Refactor the code --- .../ts/components/GraphTimeline.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphTimeline.tsx b/optuna_dashboard/ts/components/GraphTimeline.tsx index dfb97bc6..a910e494 100644 --- a/optuna_dashboard/ts/components/GraphTimeline.tsx +++ b/optuna_dashboard/ts/components/GraphTimeline.tsx @@ -56,6 +56,7 @@ const plotTimeline = (trials: Trial[], mode: string) => { Running: "green", Waiting: "gray", } + const runningKey = "Running" const lastTrials = trials.slice(-maxBars) // To only show last elements const minDatetime = new Date( @@ -66,17 +67,16 @@ const plotTimeline = (trials: Trial[], mode: string) => { ) ) const maxRunDuration = Math.max( - ...lastTrials.map((t) => { + ...trials.map((t) => { const start = t.datetime_start?.getTime() ?? new Date().getTime() const complete = t.datetime_complete?.getTime() ?? start - return complete - start + return t.state === runningKey ? -Infinity : complete - start }) ) const hasRunning = - maxRunDuration === 0 || - lastTrials.some((t) => { - const isRunning = t.state === "Running" - if (!isRunning) { + maxRunDuration === -Infinity || + trials.some((t) => { + if (t.state !== runningKey) { return false } const start = t.datetime_start?.getTime() ?? new Date().getTime() @@ -114,15 +114,14 @@ const plotTimeline = (trials: Trial[], mode: string) => { } const makeTrace = (bars: Trial[], state: string, color: string) => { + const isRunning = state === runningKey const starts = bars.map((b) => b.datetime_start ?? new Date()) - const completes = bars.map((b, i) => b.datetime_complete ?? starts[i]) - const isRunning = state === "Running" const runDurations = bars.map((b) => { const start = b.datetime_start?.getTime() ?? new Date().getTime() const complete = b.datetime_complete?.getTime() ?? start - return !isRunning ? complete - start : maxDatetime.getTime() - start + // By using 1 as the min value, we can recognize these bars at least when zooming in. + return Math.max(1, !isRunning ? complete - start : maxDatetime.getTime() - start) }) - starts.map((s, i) => Math.max(10, completes[i].getTime() - s.getTime())) const trace: Partial = { type: "bar", x: runDurations, From 895e32727aae3650df9abaa0919bca178ea5ad84 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Tue, 12 Dec 2023 14:38:33 +0100 Subject: [PATCH 4/8] Fix hasRunning The last version could get maxRunDuration of zero when we have waiting or failed trials without any complete or pruned trials. It leads to false in the judge of hasRunning. However, in this case, we should be able to say that we have running trials. I fixed this issue in this commit. --- optuna_dashboard/ts/components/GraphTimeline.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphTimeline.tsx b/optuna_dashboard/ts/components/GraphTimeline.tsx index a910e494..d710a0b1 100644 --- a/optuna_dashboard/ts/components/GraphTimeline.tsx +++ b/optuna_dashboard/ts/components/GraphTimeline.tsx @@ -68,13 +68,14 @@ const plotTimeline = (trials: Trial[], mode: string) => { ) const maxRunDuration = Math.max( ...trials.map((t) => { - const start = t.datetime_start?.getTime() ?? new Date().getTime() - const complete = t.datetime_complete?.getTime() ?? start - return t.state === runningKey ? -Infinity : complete - start + return t.datetime_start === undefined || t.datetime_complete === undefined + ? -Infinity + : t.datetime_start.getTime() - t.datetime_complete.getTime() }) ) const hasRunning = - maxRunDuration === -Infinity || + (maxRunDuration === -Infinity && + trials.some((t) => t.state === runningKey)) || trials.some((t) => { if (t.state !== runningKey) { return false @@ -120,7 +121,10 @@ const plotTimeline = (trials: Trial[], mode: string) => { const start = b.datetime_start?.getTime() ?? new Date().getTime() const complete = b.datetime_complete?.getTime() ?? start // By using 1 as the min value, we can recognize these bars at least when zooming in. - return Math.max(1, !isRunning ? complete - start : maxDatetime.getTime() - start) + return Math.max( + 1, + !isRunning ? complete - start : maxDatetime.getTime() - start + ) }) const trace: Partial = { type: "bar", From 7a1f791ced7c893f45b6c3744c2eb3e16063eac7 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Tue, 12 Dec 2023 15:10:03 +0100 Subject: [PATCH 5/8] Debug the maxDateTime --- optuna_dashboard/ts/components/GraphTimeline.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/ts/components/GraphTimeline.tsx b/optuna_dashboard/ts/components/GraphTimeline.tsx index d710a0b1..1a6e8379 100644 --- a/optuna_dashboard/ts/components/GraphTimeline.tsx +++ b/optuna_dashboard/ts/components/GraphTimeline.tsx @@ -70,7 +70,7 @@ const plotTimeline = (trials: Trial[], mode: string) => { ...trials.map((t) => { return t.datetime_start === undefined || t.datetime_complete === undefined ? -Infinity - : t.datetime_start.getTime() - t.datetime_complete.getTime() + : t.datetime_complete.getTime() - t.datetime_start.getTime() }) ) const hasRunning = From df16f7372c1f14c1248f75e1c7ed2c7011119e57 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Sun, 24 Dec 2023 08:48:15 +0100 Subject: [PATCH 6/8] Address some comments by umezawa --- optuna_dashboard/ts/components/GraphTimeline.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphTimeline.tsx b/optuna_dashboard/ts/components/GraphTimeline.tsx index 1a6e8379..b382def5 100644 --- a/optuna_dashboard/ts/components/GraphTimeline.tsx +++ b/optuna_dashboard/ts/components/GraphTimeline.tsx @@ -83,6 +83,10 @@ const plotTimeline = (trials: Trial[], mode: string) => { const start = t.datetime_start?.getTime() ?? new Date().getTime() const now = new Date().getTime() // This is an ad-hoc handling to check if the trial is running. + // We do not check via `trialState` because some trials may have state=RUNNING, + // even if they are not running because of unexpected job kills. + // In this case, we would like to ensure that these trials will not squash the timeline plot + // for the other trials. return now - start < maxRunDuration * 5 }) const maxDatetime = hasRunning @@ -117,13 +121,13 @@ const plotTimeline = (trials: Trial[], mode: string) => { const makeTrace = (bars: Trial[], state: string, color: string) => { const isRunning = state === runningKey const starts = bars.map((b) => b.datetime_start ?? new Date()) - const runDurations = bars.map((b) => { - const start = b.datetime_start?.getTime() ?? new Date().getTime() - const complete = b.datetime_complete?.getTime() ?? start + const runDurations = bars.map((b, i) => { + const startTime = starts[i].getTime() + const completeTime = b.datetime_complete?.getTime() ?? startTime // By using 1 as the min value, we can recognize these bars at least when zooming in. return Math.max( 1, - !isRunning ? complete - start : maxDatetime.getTime() - start + !isRunning ? completeTime - startTime : maxDatetime.getTime() - startTime ) }) const trace: Partial = { From ad25ad9a225df0084786c8f79d4ba4143c2d4002 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Sun, 24 Dec 2023 09:11:01 +0100 Subject: [PATCH 7/8] Refactor based on umezawa's comment --- optuna_dashboard/ts/components/GraphTimeline.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphTimeline.tsx b/optuna_dashboard/ts/components/GraphTimeline.tsx index b382def5..122f778b 100644 --- a/optuna_dashboard/ts/components/GraphTimeline.tsx +++ b/optuna_dashboard/ts/components/GraphTimeline.tsx @@ -123,12 +123,11 @@ const plotTimeline = (trials: Trial[], mode: string) => { const starts = bars.map((b) => b.datetime_start ?? new Date()) const runDurations = bars.map((b, i) => { const startTime = starts[i].getTime() - const completeTime = b.datetime_complete?.getTime() ?? startTime + const completeTime = isRunning + ? maxDatetime.getTime() + : b.datetime_complete?.getTime() ?? startTime // By using 1 as the min value, we can recognize these bars at least when zooming in. - return Math.max( - 1, - !isRunning ? completeTime - startTime : maxDatetime.getTime() - startTime - ) + return Math.max(1, completeTime - startTime) }) const trace: Partial = { type: "bar", From 1c0477e8d9f4629a88d63661cdb6ac94ae025fc9 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Tue, 26 Dec 2023 09:30:26 +0100 Subject: [PATCH 8/8] Change the waiting trials' start time --- optuna_dashboard/ts/components/GraphTimeline.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphTimeline.tsx b/optuna_dashboard/ts/components/GraphTimeline.tsx index 122f778b..eaa27277 100644 --- a/optuna_dashboard/ts/components/GraphTimeline.tsx +++ b/optuna_dashboard/ts/components/GraphTimeline.tsx @@ -80,8 +80,8 @@ const plotTimeline = (trials: Trial[], mode: string) => { if (t.state !== runningKey) { return false } - const start = t.datetime_start?.getTime() ?? new Date().getTime() const now = new Date().getTime() + const start = t.datetime_start?.getTime() ?? now // This is an ad-hoc handling to check if the trial is running. // We do not check via `trialState` because some trials may have state=RUNNING, // even if they are not running because of unexpected job kills. @@ -120,7 +120,8 @@ const plotTimeline = (trials: Trial[], mode: string) => { const makeTrace = (bars: Trial[], state: string, color: string) => { const isRunning = state === runningKey - const starts = bars.map((b) => b.datetime_start ?? new Date()) + // Waiting trials should not squash other trials, so use `maxDatetime` instead of `new Date()`. + const starts = bars.map((b) => b.datetime_start ?? maxDatetime) const runDurations = bars.map((b, i) => { const startTime = starts[i].getTime() const completeTime = isRunning