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
87 changes: 72 additions & 15 deletions CLI_fastpidc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,55 @@ function parse_args()
return args
end

# Every flag main() actually reads (see the `get`/`haskey` calls below). Kept
# in sync with those by hand, since parse_args() has no way to know which keys
# are meaningful - it happily stores (and silently drops) anything.
const VALID_ARG_KEYS = Set([
"help",
"infile",
"outfile",
"delim",
"discretizer",
"estimator",
"n-bins",
"base",
"backend",
"bb-backend",
"output-format",
"dump-mi-path",
"dump-puc-path",
"verbose",
])

"""
validate_args(args)

`parse_args` accepts any `--key value` pair, so a misspelled or
wrongly-punctuated flag was
previously stored under a key `main()` never reads, silently keeping its
default rather than erroring - a caller could ask for `--discretizer
uniform_width --n_bins 6` and get 10 bins with no indication anything was
wrong. This checks every parsed key against [`VALID_ARG_KEYS`](@ref) and
errors out, naming the likely intended flag when the mismatch is only a
`-`/`_` swap.
"""
function validate_args(args::Dict{String,String})
unknown = sort(collect(setdiff(keys(args), VALID_ARG_KEYS)))
isempty(unknown) && return nothing

lines = String[]
for key in unknown
swapped = replace(key, "-" => "_", "_" => "-")
hint = swapped in VALID_ARG_KEYS ? " (did you mean --$swapped?)" : ""
push!(lines, " --$key$hint")
end
error(
"Unrecognized command-line argument(s):\n" *
join(lines, "\n") *
"\nRun with --help to see the full list of supported arguments.",
)
end

function parse_delim(s::AbstractString)
s_l = lowercase(strip(s))
if s_l == "space" || s_l == " "
Expand Down Expand Up @@ -76,7 +125,7 @@ Basic options:
Default: 'bayesian_blocks'
--estimator STR e.g. 'maximum_likelihood'
Default: 'maximum_likelihood'
--n_bins INT Number of bins (ignored by bayesian_blocks). Default: 10
--n-bins INT Number of bins (ignored by bayesian_blocks). Default: 10
--base INT Log base for MI (2, e, 10). Default: 2

Execution / Environment:
Expand All @@ -91,7 +140,7 @@ Diagnostics Dumps:
--dump-puc-path PATH If set, dump pre-context PUC scores here (TSV).

Other:
--verbose Print detailed progress information
--verbose BOOL Print detailed progress information. Default: false
--help, -h Show this help and exit.

Example:
Expand All @@ -109,6 +158,8 @@ function main()
return
end

validate_args(args)

# ----------------- Required arguments -----------------
infile = get(args, "infile", nothing)
outfile = get(args, "outfile", nothing)
Expand All @@ -126,7 +177,7 @@ function main()
delim = parse_delim(delim_str)
discretizer = get(args, "discretizer", "bayesian_blocks")
estimator = get(args, "estimator", "maximum_likelihood")
n_bins = parse(Int, get(args, "n_bins", "10"))
n_bins = parse(Int, get(args, "n-bins", "10"))
base = parse(Int, get(args, "base", "2"))
verbose_flag = parse_bool(get(args, "verbose", "false"))

Expand Down Expand Up @@ -185,7 +236,7 @@ function main()
println(" delim = $delim_str")
println(" discretizer = $discretizer")
println(" estimator = $estimator")
println(" n_bins = $n_bins")
println(" n-bins = $n_bins")
println(" base = $base")
println(" backend = $(cfg.backend)")
println(" bb_backend = $(cfg.bb_backend)")
Expand Down Expand Up @@ -222,15 +273,21 @@ function main()
@say @sprintf("All done. Total runtime: %.1f s", t_total)
end

# Ensure we get a traceback for errors
try
main()
catch e
bt = catch_backtrace()
@say "ERROR: $(sprint(showerror, e))"
println("\nStacktrace:")
Base.show_backtrace(stdout, bt)
println()
@say "Tip: If the error mentions discretization, try --discretizer uniform_width and --n_bins 10-20."
exit(1)
# Only run when invoked as a script (`julia CLI_fastpidc.jl ...`), not when
# `include()`d - e.g. by a test file exercising `parse_args`/`validate_args`
# in isolation - which would otherwise execute `main()` against the including
# process's `ARGS` and `exit(1)` out from under it on any error.
if abspath(PROGRAM_FILE) == @__FILE__
# Ensure we get a traceback for errors
try
main()
catch e
bt = catch_backtrace()
@say "ERROR: $(sprint(showerror, e))"
println("\nStacktrace:")
Base.show_backtrace(stdout, bt)
println()
@say "Tip: If the error mentions discretization, try --discretizer uniform_width and --n-bins 10-20."
exit(1)
end
end
8 changes: 7 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,15 @@ Statistics = "1.11.1"
julia = "≥ 1.0.0"

[extras]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["DelimitedFiles", "LinearAlgebra", "Test"]
# Dates and Printf are needed because test/cli_argument_tests.jl includes
# CLI_fastpidc.jl (to exercise its argument parsing/validation in isolation),
# which `using`s both; they are otherwise resolvable stdlibs, not real deps of
# the package itself.
test = ["Dates", "DelimitedFiles", "LinearAlgebra", "Printf", "Test"]
1 change: 1 addition & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ FastPIDCCUDAExt
FastPIDCCUDAExt._kernel_source_path
FastPIDCCUDAExt._compile_ptx
FastPIDCCUDAExt._get_module
FastPIDCCUDAExt._check_kernel_scalar_limits
FastPIDCCUDAExt._bb_kernel_name
FastPIDC.bayesian_blocks_cuda_available
FastPIDC.solve_bayesian_blocks_cuda
Expand Down
Loading
Loading