From cbeed44afa76ad75aaaee432f7f0282c8e4b5881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abdo=CC=81n=20Rodri=CC=81guez?= Date: Thu, 21 May 2026 16:23:08 -0400 Subject: [PATCH 01/52] Set category to current circuits and hamiltonians --- .../circuit-models.json | 15 ++++--- .../circuit-models.json | 24 ++++++++---- data/variational-problems/hamiltonians.json | 39 ++++++++++++------- src/types/circuitModels.ts | 1 + src/types/hamiltonians.ts | 1 + 5 files changed, 54 insertions(+), 26 deletions(-) diff --git a/data/classically-verifiable-problems/circuit-models.json b/data/classically-verifiable-problems/circuit-models.json index 0930c25..b165e33 100644 --- a/data/classically-verifiable-problems/circuit-models.json +++ b/data/classically-verifiable-problems/circuit-models.json @@ -5,19 +5,22 @@ "id": "peaked_circuit_heavy_hex_49x4020", "path": "peaked_circuit_heavy_hex_49x4020.qasm", "qubits": 49, - "gates": 4020 + "gates": 4020, + "category": "Baseline Benchmarks" }, { "id": "peaked_circuit_heavy_hex_49x5072", "path": "peaked_circuit_heavy_hex_49x5072.qasm", "qubits": 49, - "gates": 5072 + "gates": 5072, + "category": "Superseded Candidates" }, { "id": "peaked_circuit_P9_Hqap_56x1917", "path": "peaked_circuit_P9_Hqap_56x1917.qasm", "qubits": 56, - "gates": 1917 + "gates": 1917, + "category": "Superseded Candidates" } ] }, @@ -27,7 +30,8 @@ "id": "random_graph_sampling_nq70_depth70_checks27_basis_fstate", "path": "nq70_depth70_checks27_basis_fstate.qasm", "qubits": 70, - "gates": 2415 + "gates": 2415, + "category": "Baseline Benchmarks" } ] }, @@ -37,7 +41,8 @@ "id": "doped_random_graph_sampling_nq70_depth70_checks27", "path": "nq70_depth70_checks27_doped.qasm", "qubits": 70, - "gates": 2415 + "gates": 2415, + "category": "Baseline Benchmarks" } ] } diff --git a/data/observable-estimations/circuit-models.json b/data/observable-estimations/circuit-models.json index 71b96ec..be91874 100644 --- a/data/observable-estimations/circuit-models.json +++ b/data/observable-estimations/circuit-models.json @@ -5,19 +5,22 @@ "id": "operator_loschmidt_echo_49x648", "path": "49Q_OLE_circuit_L_3_b_0.25_delta0.15.qasm", "qubits": 49, - "gates": 648 + "gates": 648, + "category": "Baseline Benchmarks" }, { "id": "operator_loschmidt_echo_49x1296", "path": "49Q_OLE_circuit_L_6_b_0.25_delta0.15.qasm", "qubits": 49, - "gates": 1296 + "gates": 1296, + "category": "Active Candidates" }, { "id": "operator_loschmidt_echo_70x1872", "path": "70Q_OLE_circuit_L_6_b_0.25_delta0.15.qasm", "qubits": 70, - "gates": 1872 + "gates": 1872, + "category": "Active Candidates" } ] }, @@ -27,13 +30,15 @@ "id": "SU(2)Hadron-LSH-x_100_SCV", "path": "x_100_SCV.qasm", "qubits": 120, - "gates": 60000 + "gates": 60000, + "category": "Baseline Benchmarks" }, { "id": "SU(2)Hadron-LSH-x_100_meson", "path": "x_100_meson.qasm", "qubits": 120, - "gates": 60000 + "gates": 60000, + "category": "Baseline Benchmarks" } ] }, @@ -45,7 +50,8 @@ "qubits": 51, "gates": 896, "floquet_cycles": 16, - "measured_observable": "magnetization" + "measured_observable": "magnetization", + "category": "Baseline Benchmarks" }, { "id": "floquet_mixed_field_ising_zzd2_51qx16c", @@ -53,7 +59,8 @@ "qubits": 51, "gates": 896, "floquet_cycles": 16, - "measured_observable": "ZZ_{d=2}" + "measured_observable": "ZZ_{d=2}", + "category": "Baseline Benchmarks" }, { "id": "floquet_mixed_field_ising_zzd3_51qx16c", @@ -61,7 +68,8 @@ "qubits": 51, "gates": 896, "floquet_cycles": 16, - "measured_observable": "ZZ_{d=3}" + "measured_observable": "ZZ_{d=3}", + "category": "Baseline Benchmarks" } ] } diff --git a/data/variational-problems/hamiltonians.json b/data/variational-problems/hamiltonians.json index b0d3a08..e4d3d6f 100644 --- a/data/variational-problems/hamiltonians.json +++ b/data/variational-problems/hamiltonians.json @@ -4,47 +4,56 @@ { "id": "n2_3-00_14e_28o", "path": "n2_3-00_14e_28o.fcidump", - "hilbert_space_size": 1.40e12 + "hilbert_space_size": 1.40e12, + "category": "Baseline Benchmarks" }, { "id": "n2_2-50_14e_28o", "path": "n2_2-50_14e_28o.fcidump", - "hilbert_space_size": 1.40e12 + "hilbert_space_size": 1.40e12, + "category": "Baseline Benchmarks" }, { "id": "n2_2-00_14e_28o", "path": "n2_2-00_14e_28o.fcidump", - "hilbert_space_size": 1.40e12 + "hilbert_space_size": 1.40e12, + "category": "Baseline Benchmarks" }, { "id": "n2_1-50_14e_28o", "path": "n2_1-50_14e_28o.fcidump", - "hilbert_space_size": 1.40e12 + "hilbert_space_size": 1.40e12, + "category": "Baseline Benchmarks" }, { "id": "n2_1-20_14e_28o", "path": "n2_1-20_14e_28o.fcidump", - "hilbert_space_size": 1.40e12 + "hilbert_space_size": 1.40e12, + "category": "Baseline Benchmarks" }, { "id": "n2_1-10_14e_28o", "path": "n2_1-10_14e_28o.fcidump", - "hilbert_space_size": 1.40e12 + "hilbert_space_size": 1.40e12, + "category": "Baseline Benchmarks" }, { "id": "n2_1-00_14e_28o", "path": "n2_1-00_14e_28o.fcidump", - "hilbert_space_size": 1.40e12 + "hilbert_space_size": 1.40e12, + "category": "Baseline Benchmarks" }, { "id": "n2_0-90_14e_28o", "path": "n2_0-90_14e_28o.fcidump", - "hilbert_space_size": 1.40e12 + "hilbert_space_size": 1.40e12, + "category": "Baseline Benchmarks" }, { "id": "n2_0-80_14e_28o", "path": "n2_0-80_14e_28o.fcidump", - "hilbert_space_size": 1.40e12 + "hilbert_space_size": 1.40e12, + "category": "Baseline Benchmarks" } ] }, @@ -53,7 +62,8 @@ { "id": "2fe_2s_30e_20o", "path": "2fe_2s_30e_20o.fcidump", - "hilbert_space_size": 2.40e8 + "hilbert_space_size": 2.40e8, + "category": "Baseline Benchmarks" } ] }, @@ -62,7 +72,8 @@ { "id": "4fe_4s_54e_36o", "path": "4fe_4s_54e_36o.fcidump", - "hilbert_space_size": 8.86e15 + "hilbert_space_size": 8.86e15, + "category": "Baseline Benchmarks" } ] }, @@ -71,7 +82,8 @@ { "id": "anderson_impurity_model_4i_28b_32e", "path": "anderson_impurity_model_4i_28b_32e.fcidump", - "hilbert_space_size": 3.61e17 + "hilbert_space_size": 3.61e17, + "category": "Baseline Benchmarks" } ] }, @@ -80,7 +92,8 @@ { "id": "guided_sparse_ground_state_problem_49Q_v1", "path": "49Q_v1", - "hilbert_space_size": 5.62e14 + "hilbert_space_size": 5.62e14, + "category": "Baseline Benchmarks" } ] } diff --git a/src/types/circuitModels.ts b/src/types/circuitModels.ts index dde1b3f..2c089f4 100644 --- a/src/types/circuitModels.ts +++ b/src/types/circuitModels.ts @@ -5,6 +5,7 @@ export type CircuitModels = { path: string; qubits: number; gates: number; + category: string; }[]; }; }; diff --git a/src/types/hamiltonians.ts b/src/types/hamiltonians.ts index edfa8d3..51231ed 100644 --- a/src/types/hamiltonians.ts +++ b/src/types/hamiltonians.ts @@ -4,6 +4,7 @@ export type Hamiltonians = { id: string; path: string; hilbert_space_size: number; + category: string; }[]; }; }; From 5f3d4702632de7a6e867bb09975a8850fe0f073c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abdo=CC=81n=20Rodri=CC=81guez?= Date: Thu, 21 May 2026 20:11:14 -0400 Subject: [PATCH 02/52] Filter submissions table by category and instances --- .../SubmissionsTable.tsx | 327 +++++++---------- .../SubmissionsTable.tsx | 343 ++++++++---------- .../variational-problems/SubmissionsTable.tsx | 339 +++++++---------- src/components/CategoryFilter.tsx | 37 ++ src/components/InstanceFilter.tsx | 62 ++++ src/components/ui/field.tsx | 248 +++++++++++++ src/components/ui/label.tsx | 24 ++ src/components/ui/radio-group.tsx | 45 +++ src/components/ui/separator.tsx | 28 ++ src/components/ui/tabs.tsx | 91 +++++ 10 files changed, 952 insertions(+), 592 deletions(-) create mode 100644 src/components/CategoryFilter.tsx create mode 100644 src/components/InstanceFilter.tsx create mode 100644 src/components/ui/field.tsx create mode 100644 src/components/ui/label.tsx create mode 100644 src/components/ui/radio-group.tsx create mode 100644 src/components/ui/separator.tsx create mode 100644 src/components/ui/tabs.tsx diff --git a/src/app/trackers/classically-verifiable-problems/SubmissionsTable.tsx b/src/app/trackers/classically-verifiable-problems/SubmissionsTable.tsx index 91fe300..fa8f091 100644 --- a/src/app/trackers/classically-verifiable-problems/SubmissionsTable.tsx +++ b/src/app/trackers/classically-verifiable-problems/SubmissionsTable.tsx @@ -1,16 +1,8 @@ 'use client'; +import { CATEGORIES, CategoryFilter, type Category } from '@/components/CategoryFilter'; +import { InstanceFilter } from '@/components/InstanceFilter'; import { RuntimeSeconds } from '@/components/RuntimeSeconds'; -import { Button } from '@/components/ui/button'; -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; import { Table, TableBody, @@ -22,208 +14,159 @@ import { import { TableEmptyIcon } from '@/icons'; import type { CircuitModels } from '@/types/circuitModels'; import type { CVPSubmission } from '@/types/submissions'; -import { flattenInstances, formatDate, getCircuitInstanceUrl, sortSubmissions } from '@/utils'; -import { ArrowDownIcon, RotateCcwIcon } from 'lucide-react'; +import { flattenInstances, formatDate, sortSubmissions } from '@/utils'; +import { ArrowDownIcon } from 'lucide-react'; import { useMemo, useState } from 'react'; +const DEFAULT_CATEGORY: Category = 'Active Candidates'; + export function SubmissionsTable(props: { submissions: CVPSubmission[]; circuitModels: CircuitModels; }) { const { submissions, circuitModels } = props; const circuitInstances = useMemo(() => flattenInstances(circuitModels), [circuitModels]); - const modelOptions = useMemo(() => Object.keys(circuitModels), [circuitModels]); - - const [modelFilter, setModelFilter] = useState(() => { - return modelOptions.length === 1 ? modelOptions[0] : 'all'; - }); - const [instanceFilter, setInstanceFilter] = useState(() => { - const initialModel = modelOptions.length === 1 ? modelOptions[0] : 'all'; - const initialInstances = - initialModel === 'all' ? circuitInstances : circuitModels[initialModel]?.instances || []; - return initialInstances.length === 1 ? initialInstances[0].id : 'all'; - }); - - const filteredSubmissions = useMemo(() => { - return submissions.filter((submission) => { - const instance = circuitInstances.find((inst) => inst.id === submission.circuit); - if (!instance) return false; + const [categoryFilter, setCategoryFilter] = useState(DEFAULT_CATEGORY); - const matchesModel = modelFilter === 'all' || instance.type === modelFilter; - const matchesInstance = instanceFilter === 'all' || submission.circuit === instanceFilter; + const firstInstanceOf = (category: Category) => + circuitInstances.find((inst) => inst.category === category)?.id ?? null; - return matchesModel && matchesInstance; - }); - }, [submissions, circuitInstances, modelFilter, instanceFilter]); + const [instanceFilter, setInstanceFilter] = useState(() => + firstInstanceOf(DEFAULT_CATEGORY), + ); const instanceOptions = useMemo(() => { - if (modelFilter === 'all') { - return circuitInstances; + const entriesById: Record = {}; + for (const submission of submissions) { + entriesById[submission.circuit] = (entriesById[submission.circuit] ?? 0) + 1; } - const modelInstances = circuitModels[modelFilter]?.instances || []; - return modelInstances.map((instance) => ({ ...instance, type: modelFilter })); - }, [circuitInstances, circuitModels, modelFilter]); + return circuitInstances + .filter((inst) => inst.category === categoryFilter) + .map((inst) => ({ ...inst, entries: entriesById[inst.id] ?? 0 })); + }, [submissions, circuitInstances, categoryFilter]); - const resetFilters = () => { - const newModel = modelOptions.length === 1 ? modelOptions[0] : 'all'; - setModelFilter(newModel); + const filteredSubmissions = useMemo(() => { + if (!instanceFilter) return []; + return submissions.filter((submission) => submission.circuit === instanceFilter); + }, [submissions, instanceFilter]); + + const counts = useMemo(() => { + const acc: Record = { + 'Active Candidates': 0, + 'Baseline Benchmarks': 0, + 'Superseded Candidates': 0, + }; + for (const submission of submissions) { + const instance = circuitInstances.find((inst) => inst.id === submission.circuit); + if (instance && (CATEGORIES as readonly string[]).includes(instance.category)) { + acc[instance.category as Category]++; + } + } + return acc; + }, [submissions, circuitInstances]); - const newInstances = - newModel === 'all' ? circuitInstances : circuitModels[newModel]?.instances || []; - setInstanceFilter(newInstances.length === 1 ? newInstances[0].id : 'all'); + const handleCategoryChange = (value: Category) => { + setCategoryFilter(value); + setInstanceFilter(firstInstanceOf(value)); }; return ( -
-
- - - - - +
+
+
- - - - - Date - - Name / Institutions - Method - Circuit - Qubits - Gates - Value - - Runtime -
- (seconds) -
- Compute resources -
-
- - {filteredSubmissions.length === 0 ? ( - - ) : ( - sortSubmissions(filteredSubmissions).map((submission, index) => { - const circuitInstance = circuitInstances.find( - (instance) => instance.id === submission.circuit, - )!; - - return ( - - - - - - - {submission.name} - - -
- By:{' '} - {submission.institutions} -
-
- {submission.method} - - - {circuitInstance.id} - - - {circuitInstance.qubits} - {circuitInstance.gates} - {submission.value} - -
- Q:{' '} - -
-
- C:{' '} - -
-
- -
- Q: {submission.computeResourcesQuantum || '-'} -
-
- C:{' '} - {submission.computeResourcesClassical || '-'} -
-
-
- ); - }) - )} -
-
+
+ + +
+ + + + + Date + + Name / Institutions + Method + Qubits + Gates + Value + + Runtime +
+ (seconds) +
+ Compute resources +
+
+ + {filteredSubmissions.length === 0 ? ( + + ) : ( + sortSubmissions(filteredSubmissions).map((submission, index) => { + const circuitInstance = circuitInstances.find( + (instance) => instance.id === submission.circuit, + )!; + + return ( + + + + + + + {submission.name} + + +
+ By:{' '} + {submission.institutions} +
+
+ {submission.method} + {circuitInstance.qubits} + {circuitInstance.gates} + {submission.value} + +
+ Q:{' '} + +
+
+ C:{' '} + +
+
+ +
+ Q:{' '} + {submission.computeResourcesQuantum || '-'} +
+
+ C:{' '} + {submission.computeResourcesClassical || '-'} +
+
+
+ ); + }) + )} +
+
+
+
); } @@ -231,7 +174,7 @@ export function SubmissionsTable(props: { function TableBodyEmpty() { return ( - +

There are no submissions yet

diff --git a/src/app/trackers/observable-estimations/SubmissionsTable.tsx b/src/app/trackers/observable-estimations/SubmissionsTable.tsx index 857d890..2f7399a 100644 --- a/src/app/trackers/observable-estimations/SubmissionsTable.tsx +++ b/src/app/trackers/observable-estimations/SubmissionsTable.tsx @@ -1,16 +1,8 @@ 'use client'; +import { CATEGORIES, CategoryFilter, type Category } from '@/components/CategoryFilter'; +import { InstanceFilter } from '@/components/InstanceFilter'; import { RuntimeSeconds } from '@/components/RuntimeSeconds'; -import { Button } from '@/components/ui/button'; -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; import { Table, TableBody, @@ -22,214 +14,169 @@ import { import { TableEmptyIcon } from '@/icons'; import type { CircuitModels } from '@/types/circuitModels'; import type { OESubmission } from '@/types/submissions'; -import { flattenInstances, formatDate, getCircuitInstanceUrl, sortSubmissions } from '@/utils'; -import { ArrowDownIcon, RotateCcwIcon } from 'lucide-react'; +import { flattenInstances, formatDate, sortSubmissions } from '@/utils'; +import { ArrowDownIcon } from 'lucide-react'; import { useMemo, useState } from 'react'; +const DEFAULT_CATEGORY: Category = 'Active Candidates'; + export function SubmissionsTable(props: { submissions: OESubmission[]; circuitModels: CircuitModels; }) { const { submissions, circuitModels } = props; const circuitInstances = useMemo(() => flattenInstances(circuitModels), [circuitModels]); - const modelOptions = useMemo(() => Object.keys(circuitModels), [circuitModels]); - - const [modelFilter, setModelFilter] = useState(() => { - return modelOptions.length === 1 ? modelOptions[0] : 'all'; - }); - const [instanceFilter, setInstanceFilter] = useState(() => { - const initialModel = modelOptions.length === 1 ? modelOptions[0] : 'all'; - const initialInstances = - initialModel === 'all' ? circuitInstances : circuitModels[initialModel]?.instances || []; - return initialInstances.length === 1 ? initialInstances[0].id : 'all'; - }); - - const filteredSubmissions = useMemo(() => { - return submissions.filter((submission) => { - const instance = circuitInstances.find((inst) => inst.id === submission.circuit); - if (!instance) return false; + const [categoryFilter, setCategoryFilter] = useState(DEFAULT_CATEGORY); - const matchesModel = modelFilter === 'all' || instance.type === modelFilter; - const matchesInstance = instanceFilter === 'all' || submission.circuit === instanceFilter; + const firstInstanceOf = (category: Category) => + circuitInstances.find((inst) => inst.category === category)?.id ?? null; - return matchesModel && matchesInstance; - }); - }, [submissions, circuitInstances, modelFilter, instanceFilter]); + const [instanceFilter, setInstanceFilter] = useState(() => + firstInstanceOf(DEFAULT_CATEGORY), + ); const instanceOptions = useMemo(() => { - if (modelFilter === 'all') { - return circuitInstances; + const entriesById: Record = {}; + for (const submission of submissions) { + entriesById[submission.circuit] = (entriesById[submission.circuit] ?? 0) + 1; } - const modelInstances = circuitModels[modelFilter]?.instances || []; - return modelInstances.map((instance) => ({ ...instance, type: modelFilter })); - }, [circuitInstances, circuitModels, modelFilter]); + return circuitInstances + .filter((inst) => inst.category === categoryFilter) + .map((inst) => ({ ...inst, entries: entriesById[inst.id] ?? 0 })); + }, [submissions, circuitInstances, categoryFilter]); - const resetFilters = () => { - const newModel = modelOptions.length === 1 ? modelOptions[0] : 'all'; - setModelFilter(newModel); + const filteredSubmissions = useMemo(() => { + if (!instanceFilter) return []; + return submissions.filter((submission) => submission.circuit === instanceFilter); + }, [submissions, instanceFilter]); + + const counts = useMemo(() => { + const acc: Record = { + 'Active Candidates': 0, + 'Baseline Benchmarks': 0, + 'Superseded Candidates': 0, + }; + for (const submission of submissions) { + const instance = circuitInstances.find((inst) => inst.id === submission.circuit); + if (instance && (CATEGORIES as readonly string[]).includes(instance.category)) { + acc[instance.category as Category]++; + } + } + return acc; + }, [submissions, circuitInstances]); - const newInstances = - newModel === 'all' ? circuitInstances : circuitModels[newModel]?.instances || []; - setInstanceFilter(newInstances.length === 1 ? newInstances[0].id : 'all'); + const handleCategoryChange = (value: Category) => { + setCategoryFilter(value); + setInstanceFilter(firstInstanceOf(value)); }; return ( -
-
- - - - - +
+
+
- - - - - Date - - Name / Institutions - Method - Circuit - Qubits - Gates - - Expectation value -
- [upper, lower bound] -
- - Runtime -
- (seconds) -
- Compute resources -
-
- - {filteredSubmissions.length === 0 ? ( - - ) : ( - sortSubmissions(filteredSubmissions).map((submission, index) => { - const circuitInstance = circuitInstances.find( - (instance) => instance.id === submission.circuit, - )!; - - return ( - - - - - - - {submission.name} - - -
- By:{' '} - {submission.institutions} -
-
- {submission.method} - - - {circuitInstance.id} - - - {circuitInstance.qubits} - {circuitInstance.gates} - -
{submission.observableValue}
-
- [{submission.errorBoundHigh || 'N/A'}, {submission.errorBoundLow || 'N/A'}] -
-
- -
- Q:{' '} - -
-
- C:{' '} - -
-
- -
- Q: {submission.computeResourcesQuantum || '-'} -
-
- C:{' '} - {submission.computeResourcesClassical || '-'} -
-
-
- ); - }) - )} -
-
+
+ + +
+ + + + + Date + + Name / Institutions + Method + Qubits + Gates + + Expectation value +
+ [upper, lower bound] +
+ + Runtime +
+ (seconds) +
+ Compute resources +
+
+ + {filteredSubmissions.length === 0 ? ( + + ) : ( + sortSubmissions(filteredSubmissions).map((submission, index) => { + const circuitInstance = circuitInstances.find( + (instance) => instance.id === submission.circuit, + )!; + + return ( + + + + + + + {submission.name} + + +
+ By:{' '} + {submission.institutions} +
+
+ {submission.method} + {circuitInstance.qubits} + {circuitInstance.gates} + +
{submission.observableValue}
+
+ [{submission.errorBoundHigh || 'N/A'},{' '} + {submission.errorBoundLow || 'N/A'}] +
+
+ +
+ Q:{' '} + +
+
+ C:{' '} + +
+
+ +
+ Q:{' '} + {submission.computeResourcesQuantum || '-'} +
+
+ C:{' '} + {submission.computeResourcesClassical || '-'} +
+
+
+ ); + }) + )} +
+
+
+
); } @@ -237,7 +184,7 @@ export function SubmissionsTable(props: { function TableBodyEmpty() { return ( - +

There are no submissions yet

diff --git a/src/app/trackers/variational-problems/SubmissionsTable.tsx b/src/app/trackers/variational-problems/SubmissionsTable.tsx index 0c60773..833af10 100644 --- a/src/app/trackers/variational-problems/SubmissionsTable.tsx +++ b/src/app/trackers/variational-problems/SubmissionsTable.tsx @@ -1,16 +1,8 @@ 'use client'; +import { CATEGORIES, CategoryFilter, type Category } from '@/components/CategoryFilter'; +import { InstanceFilter } from '@/components/InstanceFilter'; import { RuntimeSeconds } from '@/components/RuntimeSeconds'; -import { Button } from '@/components/ui/button'; -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; import { Table, TableBody, @@ -22,218 +14,161 @@ import { import { TableEmptyIcon } from '@/icons'; import type { Hamiltonians } from '@/types/hamiltonians'; import type { VPSubmission } from '@/types/submissions'; -import { flattenInstances, formatDate, getHamiltonianUrl, sortSubmissions } from '@/utils'; -import { ArrowDownIcon, RotateCcwIcon } from 'lucide-react'; +import { flattenInstances, formatDate, sortSubmissions } from '@/utils'; +import { ArrowDownIcon } from 'lucide-react'; import { useMemo, useState } from 'react'; +const DEFAULT_CATEGORY: Category = 'Active Candidates'; + export function SubmissionsTable(props: { submissions: VPSubmission[]; hamiltonians: Hamiltonians; }) { const { submissions, hamiltonians } = props; const hamiltonianInstances = useMemo(() => flattenInstances(hamiltonians), [hamiltonians]); - const hamiltonianOptions = useMemo(() => Object.keys(hamiltonians), [hamiltonians]); - - const [hamiltonianFilter, setHamiltonianFilter] = useState(() => { - return hamiltonianOptions.length === 1 ? hamiltonianOptions[0] : 'all'; - }); - const [instanceFilter, setInstanceFilter] = useState(() => { - const initialHamiltonian = hamiltonianOptions.length === 1 ? hamiltonianOptions[0] : 'all'; - const initialInstances = - initialHamiltonian === 'all' - ? hamiltonianInstances - : hamiltonians[initialHamiltonian]?.instances || []; - return initialInstances.length === 1 ? initialInstances[0].id : 'all'; - }); + const [categoryFilter, setCategoryFilter] = useState(DEFAULT_CATEGORY); - const filteredSubmissions = useMemo(() => { - return submissions.filter((submission) => { - const instance = hamiltonianInstances.find((inst) => inst.id === submission.hamiltonian); - if (!instance) return false; + const firstInstanceOf = (category: Category) => + hamiltonianInstances.find((inst) => inst.category === category)?.id ?? null; - const matchesHamiltonian = hamiltonianFilter === 'all' || instance.type === hamiltonianFilter; - const matchesInstance = instanceFilter === 'all' || submission.hamiltonian === instanceFilter; - - return matchesHamiltonian && matchesInstance; - }); - }, [submissions, hamiltonianInstances, hamiltonianFilter, instanceFilter]); + const [instanceFilter, setInstanceFilter] = useState(() => + firstInstanceOf(DEFAULT_CATEGORY), + ); const instanceOptions = useMemo(() => { - if (hamiltonianFilter === 'all') { - return hamiltonianInstances; + const entriesById: Record = {}; + for (const submission of submissions) { + entriesById[submission.hamiltonian] = (entriesById[submission.hamiltonian] ?? 0) + 1; } - const filteredInstances = hamiltonians[hamiltonianFilter]?.instances || []; - return filteredInstances.map((instance) => ({ ...instance, type: hamiltonianFilter })); - }, [hamiltonianInstances, hamiltonians, hamiltonianFilter]); + return hamiltonianInstances + .filter((inst) => inst.category === categoryFilter) + .map((inst) => ({ ...inst, entries: entriesById[inst.id] ?? 0 })); + }, [submissions, hamiltonianInstances, categoryFilter]); - const resetFilters = () => { - const newHamiltonian = hamiltonianOptions.length === 1 ? hamiltonianOptions[0] : 'all'; - setHamiltonianFilter(newHamiltonian); + const filteredSubmissions = useMemo(() => { + if (!instanceFilter) return []; + return submissions.filter((submission) => submission.hamiltonian === instanceFilter); + }, [submissions, instanceFilter]); + + const counts = useMemo(() => { + const acc: Record = { + 'Active Candidates': 0, + 'Baseline Benchmarks': 0, + 'Superseded Candidates': 0, + }; + for (const submission of submissions) { + const instance = hamiltonianInstances.find((inst) => inst.id === submission.hamiltonian); + if (instance && (CATEGORIES as readonly string[]).includes(instance.category)) { + acc[instance.category as Category]++; + } + } + return acc; + }, [submissions, hamiltonianInstances]); - const newInstances = - newHamiltonian === 'all' - ? hamiltonianInstances - : hamiltonians[newHamiltonian]?.instances || []; - setInstanceFilter(newInstances.length === 1 ? newInstances[0].id : 'all'); + const handleCategoryChange = (value: Category) => { + setCategoryFilter(value); + setInstanceFilter(firstInstanceOf(value)); }; return ( -
-
- - - - - +
+
+
- - - - - Date - - Name / Institutions - Method - Hamiltonian - Qubits - Gates - - Energy (Eh) -
- [upper, lower bound] -
- - Runtime -
- (seconds) -
- Compute resources -
-
- - {filteredSubmissions.length === 0 ? ( - - ) : ( - sortSubmissions(filteredSubmissions).map((submission, index) => { - const hamiltonianInstance = hamiltonianInstances.find( - (instance) => instance.id === submission.hamiltonian, - )!; - - return ( - - - - - - - {submission.name} - - -
- By:{' '} - {submission.institutions} -
-
- {submission.method} - - - {submission.hamiltonian} - - - {submission.qubits} - {submission.gates} - -
{submission.energy}
-
- [{submission.errorBoundHigh || 'N/A'}, {submission.errorBoundLow || 'N/A'}] -
-
- -
- Q:{' '} - -
-
- C:{' '} - -
-
- -
- Q: {submission.computeResourcesQuantum || '-'} -
-
- C:{' '} - {submission.computeResourcesClassical || '-'} -
-
-
- ); - }) - )} -
-
+
+ + +
+ + + + + Date + + Name / Institutions + Method + Qubits + Gates + + Energy (Eh) +
+ [upper, lower bound] +
+ + Runtime +
+ (seconds) +
+ Compute resources +
+
+ + {filteredSubmissions.length === 0 ? ( + + ) : ( + sortSubmissions(filteredSubmissions).map((submission, index) => ( + + + + + + + {submission.name} + + +
+ By:{' '} + {submission.institutions} +
+
+ {submission.method} + {submission.qubits} + {submission.gates} + +
{submission.energy}
+
+ [{submission.errorBoundHigh || 'N/A'}, {submission.errorBoundLow || 'N/A'}] +
+
+ +
+ Q:{' '} + +
+
+ C:{' '} + +
+
+ +
+ Q: {submission.computeResourcesQuantum || '-'} +
+
+ C:{' '} + {submission.computeResourcesClassical || '-'} +
+
+
+ )) + )} +
+
+
+
); } @@ -241,7 +176,7 @@ export function SubmissionsTable(props: { function TableBodyEmpty() { return ( - +

There are no submissions yet

diff --git a/src/components/CategoryFilter.tsx b/src/components/CategoryFilter.tsx new file mode 100644 index 0000000..74874b8 --- /dev/null +++ b/src/components/CategoryFilter.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; + +export const CATEGORIES = [ + 'Active Candidates', + 'Baseline Benchmarks', + 'Superseded Candidates', +] as const; + +export type Category = (typeof CATEGORIES)[number]; + +const LABELS: Record = { + 'Active Candidates': 'Active', + 'Baseline Benchmarks': 'Baseline', + 'Superseded Candidates': 'Superseded', +}; + +export function CategoryFilter(props: { + value: Category; + onChange: (value: Category) => void; + counts: Record; +}) { + const { value, onChange, counts } = props; + + return ( + onChange(v as Category)}> + + {CATEGORIES.map((category) => ( + + {LABELS[category]} ({counts[category]}) + + ))} + + + ); +} diff --git a/src/components/InstanceFilter.tsx b/src/components/InstanceFilter.tsx new file mode 100644 index 0000000..d62ac80 --- /dev/null +++ b/src/components/InstanceFilter.tsx @@ -0,0 +1,62 @@ +'use client'; + +import { + Field, + FieldContent, + FieldDescription, + FieldLabel, + FieldTitle, +} from '@/components/ui/field'; +import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; + +type Instance = { id: string; type: string; entries: number }; + +function stripType(id: string, type: string): string { + if (!id.startsWith(type)) return id; + return id.slice(type.length).replace(/^[_-]+/, ''); +} + +export function InstanceFilter(props: { + instances: T[]; + value: string | null; + onChange: (id: string) => void; +}) { + const { instances, value, onChange } = props; + + return ( + + ); +} diff --git a/src/components/ui/field.tsx b/src/components/ui/field.tsx new file mode 100644 index 0000000..ec849da --- /dev/null +++ b/src/components/ui/field.tsx @@ -0,0 +1,248 @@ +"use client" + +import { useMemo } from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" +import { Label } from "@/components/ui/label" +import { Separator } from "@/components/ui/separator" + +function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) { + return ( +
[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3", + className + )} + {...props} + /> + ) +} + +function FieldLegend({ + className, + variant = "legend", + ...props +}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) { + return ( + + ) +} + +function FieldGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
[data-slot=field-group]]:gap-4", + className + )} + {...props} + /> + ) +} + +const fieldVariants = cva( + "group/field flex w-full gap-3 data-[invalid=true]:text-destructive", + { + variants: { + orientation: { + vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"], + horizontal: [ + "flex-row items-center", + "[&>[data-slot=field-label]]:flex-auto", + "has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + responsive: [ + "flex-col @md/field-group:flex-row @md/field-group:items-center [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto", + "@md/field-group:[&>[data-slot=field-label]]:flex-auto", + "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px", + ], + }, + }, + defaultVariants: { + orientation: "vertical", + }, + } +) + +function Field({ + className, + orientation = "vertical", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function FieldContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function FieldLabel({ + className, + ...props +}: React.ComponentProps) { + return ( +