diff --git a/src/hictk/cli/cli_dump.cpp b/src/hictk/cli/cli_dump.cpp index 923b0fb6..aa606c90 100644 --- a/src/hictk/cli/cli_dump.cpp +++ b/src/hictk/cli/cli_dump.cpp @@ -120,7 +120,7 @@ void Cli::make_dump_subcommand() { void Cli::validate_dump_subcommand() const { assert(_cli.get_subcommand("dump")->parsed()); - [[maybe_unused]] std::vector warnings; + std::vector warnings; std::vector errors; const auto& c = std::get(_config); diff --git a/src/hictk/cli/cli_zoomify.cpp b/src/hictk/cli/cli_zoomify.cpp index e569edbd..457b3c0d 100644 --- a/src/hictk/cli/cli_zoomify.cpp +++ b/src/hictk/cli/cli_zoomify.cpp @@ -45,14 +45,23 @@ void Cli::make_zoomify_subcommand() { sc.add_option( "--resolutions", c.resolutions, - "One or more resolution to be used for coarsening.") - ->required(true); + "One or more resolution to be used for coarsening."); sc.add_flag( "--copy-base-resolution,!--no-copy-base-resolution", c.copy_base_resolution, "Copy the base resolution to the output file."); + sc.add_flag( + "--nice-steps,!--pow2-steps", + c.nice_resolution_steps, + "Use nice or power of two steps to automatically generate the list of resolutions.\n" + "Example:\n" + "Base resolution: 1000\n" + "Pow2: 1000, 2000, 4000, 8000...\n" + "Nice: 1000, 2000, 5000, 10000...\n") + ->default_str("--nice-steps"); + sc.add_option( "-v,--verbosity", c.verbosity, @@ -102,6 +111,7 @@ static std::vector detect_invalid_resolutions( void Cli::validate_zoomify_subcommand() const { assert(_cli.get_subcommand("zoomify")->parsed()); + std::vector warnings; std::vector errors; const auto& c = std::get(_config); @@ -126,6 +136,19 @@ void Cli::validate_zoomify_subcommand() const { fmt::join(invalid, "\n - "), clr.bin_size())); } + const auto* sc = _cli.get_subcommand("zoomify"); + const auto nice_or_pow2_steps_parsed = + !sc->get_option("--nice-steps")->empty() || !sc->get_option("--pow2-steps")->empty(); + if (!c.resolutions.empty() && nice_or_pow2_steps_parsed) { + warnings.emplace_back( + "--nice-steps and --pow2-steps are ignored when resolutions are explicitly set with " + "--resolutions."); + } + + for (const auto& w : warnings) { + SPDLOG_WARN(FMT_STRING("{}"), w); + } + if (!errors.empty()) { throw std::runtime_error( fmt::format(FMT_STRING("the following error(s) where encountered while validating CLI " @@ -134,6 +157,45 @@ void Cli::validate_zoomify_subcommand() const { } } +static std::vector generate_resolutions_pow2( + std::uint32_t base_resolution, std::uint32_t upper_bound = 10'000'000) { + assert(base_resolution != 0); + std::vector resolutions{base_resolution}; + + for (auto res = resolutions.back(); res * 2 <= upper_bound; res = resolutions.back()) { + resolutions.push_back(res * 2); + } + + return resolutions; +} + +static std::vector generate_resolutions_nice( + std::uint32_t base_resolution, std::uint32_t upper_bound = 10'000'000) { + assert(base_resolution != 0); + std::vector resolutions{base_resolution}; + + while (resolutions.back() * 2 <= upper_bound) { + const auto res = resolutions.back(); + + if (res * 2 > upper_bound) { + break; + } + resolutions.push_back(res * 2); + + if (res * 5 > upper_bound) { + break; + } + resolutions.push_back(res * 5); + + if (res * 10 > upper_bound) { + break; + } + resolutions.push_back(res * 10); + } + + return resolutions; +} + void Cli::transform_args_zoomify_subcommand() { auto& c = std::get(_config); @@ -146,7 +208,12 @@ void Cli::transform_args_zoomify_subcommand() { c.output_path = std::filesystem::path(clr.path()).replace_extension(".mcool").string(); } - std::sort(c.resolutions.begin(), c.resolutions.end()); + if (c.resolutions.empty()) { + c.resolutions = c.nice_resolution_steps ? generate_resolutions_nice(clr.bin_size()) + : generate_resolutions_pow2(clr.bin_size()); + } else { + std::sort(c.resolutions.begin(), c.resolutions.end()); + } if (c.resolutions.front() != clr.bin_size()) { c.resolutions.insert(c.resolutions.begin(), clr.bin_size()); diff --git a/src/hictk/include/hictk/tools/config.hpp b/src/hictk/include/hictk/tools/config.hpp index a02876d9..2b1955a5 100644 --- a/src/hictk/include/hictk/tools/config.hpp +++ b/src/hictk/include/hictk/tools/config.hpp @@ -97,6 +97,7 @@ struct ZoomifyConfig { std::vector resolutions{}; bool copy_base_resolution{true}; + bool nice_resolution_steps{true}; bool force{false}; std::uint8_t verbosity{4}; diff --git a/test/scripts/hictk_zoomify.sh b/test/scripts/hictk_zoomify.sh index 991531de..6e64e686 100755 --- a/test/scripts/hictk_zoomify.sh +++ b/test/scripts/hictk_zoomify.sh @@ -91,8 +91,7 @@ trap 'rm -rf -- "$outdir"' EXIT "$hictk_bin" zoomify \ "$ref_cooler::/resolutions/${resolutions[0]}" \ - "$outdir/out.mcool" \ - --resolutions "${resolutions[@]}" + "$outdir/out.mcool" for res in "${resolutions[@]}"; do if ! compare_coolers \