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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ hypergraph

CPU.*
results
generated-datasets
cosmos-export
.vercel
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"benchmark1": "bun run --cpu-prof-md scripts/profiling/hg07-first10.ts",
"benchmark:bus": "./benchmark-bus.sh",
"benchmark:srj13": "./benchmark-srj13.sh",
"generate:srj18": "bun run scripts/benchmarking/generate-srj18.ts",
"benchmark:section": "bun run scripts/benchmarking/hg07-section-pipeline.ts",
"benchmark:section:profile": "bun run scripts/benchmarking/hg07-first40-section-profile.ts",
"benchmark:port-point-pathing": "bun run scripts/benchmarking/port-point-pathing-section-pipeline.ts"
Expand All @@ -34,6 +35,8 @@
"typescript": "^5"
},
"dependencies": {
"@tsci/seveibar.dataset-srj13": "https://github.com/tscircuit/dataset-srj13#161a835ed40ce72a2fa18ffe5ba6ba6392fcc176"
"@tsci/seveibar.dataset-srj13": "https://github.com/tscircuit/dataset-srj13#161a835ed40ce72a2fa18ffe5ba6ba6392fcc176",
"@tscircuit/capacity-autorouter": "^0.0.557",
"dataset-srj18": "git+https://github.com/tscircuit/dataset-srj18.git#48cd2f8f651bc9213a339580fdf125d2a1dd9254"
}
}
181 changes: 181 additions & 0 deletions pages/dataset-srj18.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import type { SerializedHyperGraph } from "@tscircuit/hypergraph"
import { type ChangeEvent, useEffect, useMemo, useState } from "react"
import { TinyHyperGraphSectionPipelineSolver } from "lib/index"
import { Debugger } from "./components/Debugger"

type ImportGlob = <T>(
pattern: string,
options?: { import?: string },
) => Record<string, () => Promise<T>>

const SAMPLE_HASH_PARAM = "sample"
const SAMPLE_NAMES = Array.from(
{ length: 16 },
(_, index) => `sample${String(index + 1).padStart(3, "0")}`,
)
const srj18SampleLoaders = (
import.meta as ImportMeta & { glob: ImportGlob }
).glob<SerializedHyperGraph>("../generated-datasets/srj18/*.hg.json", {
import: "default",
})

const clampSampleIndex = (sampleIndex: number) =>
Math.min(
Math.max(Number.isFinite(sampleIndex) ? sampleIndex : 0, 0),
SAMPLE_NAMES.length - 1,
)

const getSampleIndexFromHash = () => {
if (typeof window === "undefined") return 0

const hashParams = new URLSearchParams(window.location.hash.slice(1))
const sampleNumber = Number(hashParams.get(SAMPLE_HASH_PARAM))

if (!Number.isFinite(sampleNumber)) return 0
return clampSampleIndex(sampleNumber - 1)
}

const setSampleIndexInHash = (sampleIndex: number) => {
if (typeof window === "undefined") return

const url = new URL(window.location.href)
const hashParams = new URLSearchParams(url.hash.slice(1))
hashParams.set(SAMPLE_HASH_PARAM, String(sampleIndex + 1))
url.hash = hashParams.toString()

window.history.replaceState(window.history.state, "", url)
}

const getSampleLoader = (sampleName: string) =>
srj18SampleLoaders[`../generated-datasets/srj18/${sampleName}.hg.json`]

export default function DatasetSrj18Page() {
const [selectedSampleIndex, setSelectedSampleIndex] = useState(
getSampleIndexFromHash,
)
const [serializedHyperGraph, setSerializedHyperGraph] =
useState<SerializedHyperGraph | null>(null)
const [loadError, setLoadError] = useState<string | null>(null)

useEffect(() => {
const syncSelectedSampleFromHash = () => {
setSelectedSampleIndex(getSampleIndexFromHash())
}

window.addEventListener("hashchange", syncSelectedSampleFromHash)
return () =>
window.removeEventListener("hashchange", syncSelectedSampleFromHash)
}, [])

useEffect(() => {
setSampleIndexInHash(selectedSampleIndex)
}, [selectedSampleIndex])

const selectedSampleName =
SAMPLE_NAMES[selectedSampleIndex] ?? SAMPLE_NAMES[0]!
const generatedSampleNames = useMemo(
() =>
Object.keys(srj18SampleLoaders)
.map((samplePath) => /\/(sample\d+)\.hg\.json$/.exec(samplePath)?.[1])
.filter((sampleName): sampleName is string => Boolean(sampleName))
.sort(),
[],
)

useEffect(() => {
let cancelled = false
setSerializedHyperGraph(null)
setLoadError(null)

const loadSample = async () => {
const load = getSampleLoader(selectedSampleName)
if (!load) {
throw new Error(`Missing generated srj18 sample: ${selectedSampleName}`)
}

const nextSerializedHyperGraph = await load()
if (!cancelled) {
setSerializedHyperGraph(nextSerializedHyperGraph)
}
}

loadSample().catch((error) => {
if (!cancelled) {
setLoadError(error instanceof Error ? error.message : String(error))
}
})

return () => {
cancelled = true
}
}, [selectedSampleName])

return (
<div className="flex h-screen flex-col gap-3 p-3">
<div className="flex flex-wrap items-center gap-3 rounded border border-slate-300 bg-white p-3">
<label className="flex items-center gap-2 text-sm">
<span className="font-medium">Sample</span>
<input
className="w-24 rounded border border-slate-300 px-2 py-1"
type="number"
min={1}
max={SAMPLE_NAMES.length}
value={selectedSampleIndex + 1}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setSelectedSampleIndex(
clampSampleIndex(Number(event.currentTarget.value) - 1),
)
}}
/>
</label>
<button
className="rounded border border-slate-300 px-3 py-1 text-sm disabled:opacity-50"
disabled={selectedSampleIndex === 0}
onClick={() =>
setSelectedSampleIndex((index) => Math.max(index - 1, 0))
}
type="button"
>
Previous
</button>
<button
className="rounded border border-slate-300 px-3 py-1 text-sm disabled:opacity-50"
disabled={selectedSampleIndex === SAMPLE_NAMES.length - 1}
onClick={() =>
setSelectedSampleIndex((index) =>
Math.min(index + 1, SAMPLE_NAMES.length - 1),
)
}
type="button"
>
Next
</button>
<div className="text-sm text-slate-700">
{selectedSampleName} • srj18 • generated {generatedSampleNames.length}
/{SAMPLE_NAMES.length}
</div>
</div>
<div className="min-h-0 flex-1">
{loadError ? (
<div className="rounded border border-amber-300 bg-amber-50 p-3 text-sm text-amber-900">
{loadError}
</div>
) : serializedHyperGraph ? (
<Debugger
key={selectedSampleName}
serializedHyperGraph={serializedHyperGraph}
createSolver={(nextSerializedHyperGraph) =>
new TinyHyperGraphSectionPipelineSolver({
serializedHyperGraph: nextSerializedHyperGraph,
})
}
/>
) : (
<div className="rounded border border-slate-300 bg-white p-3 text-sm text-slate-700">
Loading {selectedSampleName}
</div>
)}
</div>
</div>
)
}
110 changes: 103 additions & 7 deletions scripts/benchmarking/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
stackGraphicsHorizontally,
type GraphicsObject,
} from "graphics-debug"
import { mkdir, readdir, writeFile } from "node:fs/promises"
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises"
import path from "node:path"
import { loadSerializedHyperGraph } from "../../lib/compat/loadSerializedHyperGraph"
import {
Expand Down Expand Up @@ -54,14 +54,16 @@ type BenchmarkSampleResult = {
}

type SolverVariant = "core" | "poly"
type DatasetKey = "hg07" | "srj18"

const IMPROVEMENT_EPSILON = 1e-9

const HELP_TEXT = `Usage: ./benchmark.sh [options]

Run the hg07 section-pipeline benchmark and write per-sample artifacts under ./results/runNNN/.
Run the section-pipeline benchmark and write per-sample artifacts under ./results/runNNN/.

Options:
--dataset NAME Dataset to run: hg07 or 18/srj18. Defaults to hg07.
--limit N Run the first N samples from the dataset.
--sample NUM Run a specific sample by number or name (e.g. 2, 002, sample002).
--solver NAME Solver variant: core or poly. Defaults to core.
Expand All @@ -71,6 +73,7 @@ Options:

Examples:
./benchmark.sh
./benchmark.sh --dataset 18
./benchmark.sh --limit 20
./benchmark.sh --limit 20 --solver poly
./benchmark.sh --sample 2
Expand Down Expand Up @@ -163,6 +166,7 @@ const parseArgs = () => {
let sampleName: string | null = null
let candidateFamilies: TinyHyperGraphSectionCandidateFamily[] | null = null
let solverVariant: SolverVariant = "core"
let datasetKey: DatasetKey = "hg07"

for (let index = 0; index < process.argv.length; index += 1) {
const arg = process.argv[index]
Expand All @@ -185,6 +189,20 @@ const parseArgs = () => {
continue
}

if (arg === "--dataset") {
const rawValue = process.argv[index + 1]
if (rawValue === "hg07" || rawValue === "7") {
datasetKey = "hg07"
} else if (rawValue === "18" || rawValue === "srj18") {
datasetKey = "srj18"
} else {
usageError(`Invalid --dataset value: ${rawValue ?? "<missing>"}`)
}

index += 1
continue
}

if (arg === "--sample") {
const rawValue = process.argv[index + 1]
if (!rawValue) {
Expand Down Expand Up @@ -227,7 +245,7 @@ const parseArgs = () => {
usageError("Use either --limit or --sample, not both")
}

return { limit, sampleName, candidateFamilies, solverVariant }
return { limit, sampleName, candidateFamilies, solverVariant, datasetKey }
}

const formatSeconds = (durationMs: number) =>
Expand Down Expand Up @@ -299,7 +317,7 @@ const getNextRunDirectory = async (resultsDir: string) => {
}
}

const loadDatasetModule = async (): Promise<DatasetModule> => {
const loadHg07DatasetModule = async (): Promise<DatasetModule> => {
console.log("loading dataset=hg07")
const datasetModule = (await import("dataset-hg07")) as DatasetModule
console.log(
Expand All @@ -308,6 +326,78 @@ const loadDatasetModule = async (): Promise<DatasetModule> => {
return datasetModule
}

const loadSrj18DatasetModule = async (
cwd: string,
limit: number | null,
sampleName: string | null,
): Promise<DatasetModule> => {
const {
ensureSrj18DatasetGenerated,
getSrj18DatasetDir,
getSrj18SampleNames,
} = await import("./generate-srj18")

const allSampleNames = getSrj18SampleNames()
if (sampleName && !allSampleNames.includes(sampleName)) {
usageError(`Unknown sample: ${sampleName}`)
}

const requestedSampleNames = sampleName
? [sampleName]
: allSampleNames.slice(
0,
limit === null
? allSampleNames.length
: Math.min(limit, allSampleNames.length),
)

await ensureSrj18DatasetGenerated(cwd, requestedSampleNames)

const datasetDir = getSrj18DatasetDir(cwd)
console.log(`loading dataset=srj18 dir=${datasetDir}`)

const datasetModule: DatasetModule = {
manifest: {
sampleCount: allSampleNames.length,
samples: allSampleNames.map((srj18SampleName) => ({
sampleName: srj18SampleName,
circuitKey: "srj18",
circuitId: srj18SampleName,
stepsToPortPointSolve: 0,
})),
},
}

for (const srj18SampleName of requestedSampleNames) {
const serializedHyperGraph = JSON.parse(
await readFile(
path.join(datasetDir, `${srj18SampleName}.hg.json`),
"utf8",
),
) as SerializedHyperGraph

datasetModule[srj18SampleName] = serializedHyperGraph
}

console.log(
`loaded dataset=srj18 samples=${datasetModule.manifest.sampleCount}`,
)
return datasetModule
}

const loadDatasetModule = async (
datasetKey: DatasetKey,
cwd: string,
limit: number | null,
sampleName: string | null,
): Promise<DatasetModule> => {
if (datasetKey === "srj18") {
return loadSrj18DatasetModule(cwd, limit, sampleName)
}

return loadHg07DatasetModule()
}

const getSelectedSamples = (
datasetModule: DatasetModule,
limit: number | null,
Expand Down Expand Up @@ -360,9 +450,15 @@ const stringifyLogValue = (value: unknown) =>
typeof value === "string" ? value : JSON.stringify(value, null, 2)

const main = async () => {
const { limit, sampleName, candidateFamilies, solverVariant } = parseArgs()
const datasetModule = await loadDatasetModule()
const cwd = process.cwd()
const { limit, sampleName, candidateFamilies, solverVariant, datasetKey } =
parseArgs()
const datasetModule = await loadDatasetModule(
datasetKey,
cwd,
limit,
sampleName,
)
const resultsDir = path.join(cwd, "results")
const { runName } = await getNextRunDirectory(resultsDir)
const runDir = path.join(resultsDir, runName)
Expand All @@ -375,7 +471,7 @@ const main = async () => {
: TinyHyperGraphSectionPipelineSolver

console.log(
`dataset=hg07 samples=${sampleMetas.length}/${datasetModule.manifest.sampleCount} run=${runName} solver=${solverVariant} families=${candidateFamilies?.join(",") ?? "default"}`,
`dataset=${datasetKey} samples=${sampleMetas.length}/${datasetModule.manifest.sampleCount} run=${runName} solver=${solverVariant} families=${candidateFamilies?.join(",") ?? "default"}`,
)

for (const sampleMeta of sampleMetas) {
Expand Down
Loading
Loading