Alien-libhisto

 view release on metacpan or  search on metacpan

bundled/tools/src/cmd_fit.c  view on Meta::CPAN

    fprintf(out, "}\n");
}

#include "cli_opt.h"

typedef struct {
    double lower_bounds[16];
    double upper_bounds[16];
    bool fixed_params[16];
    bool has_lower;
    bool has_upper;
    bool has_fixed;
    double range_min;
    double range_max;
} fit_bounds_ctx_t;

static int cb_parse_bounds(const char *opt_name, const char *val, void *data, char *err_buf, size_t err_size) {
    (void)opt_name;
    fit_bounds_ctx_t *ctx = (fit_bounds_ctx_t *)data;
    int pidx = 0;
    double bmin = 0.0, bmax = 0.0;
    if (!val || sscanf(val, "%d=%lf:%lf", &pidx, &bmin, &bmax) != 3 || pidx < 0 || pidx >= 16) {
        snprintf(err_buf, err_size, "Bounds must be in IDX=MIN:MAX format (e.g. --bounds=2=0.1:10.0)");
        return 1;
    }
    ctx->lower_bounds[pidx] = bmin;
    ctx->upper_bounds[pidx] = bmax;
    ctx->has_lower = true;
    ctx->has_upper = true;
    return 0;
}

static int cb_parse_fix_param(const char *opt_name, const char *val, void *data, char *err_buf, size_t err_size) {
    (void)opt_name;
    fit_bounds_ctx_t *ctx = (fit_bounds_ctx_t *)data;
    int pidx = 0;
    double fval = 0.0;
    if (!val || sscanf(val, "%d=%lf", &pidx, &fval) != 2 || pidx < 0 || pidx >= 16) {
        snprintf(err_buf, err_size, "Fix-param must be in IDX=VAL format (e.g. --fix-param=1=0.0)");
        return 1;
    }
    ctx->lower_bounds[pidx] = fval;
    ctx->upper_bounds[pidx] = fval;
    ctx->fixed_params[pidx] = true;
    ctx->has_fixed = true;
    return 0;
}

static int cb_parse_range(const char *opt_name, const char *val, void *data, char *err_buf, size_t err_size) {
    (void)opt_name;
    fit_bounds_ctx_t *ctx = (fit_bounds_ctx_t *)data;
    if (!val || sscanf(val, "%lf:%lf", &ctx->range_min, &ctx->range_max) != 2) {
        snprintf(err_buf, err_size, "Range must be in MIN:MAX format (e.g. --range=0:100)");
        return 1;
    }
    return 0;
}

int histo_cli_fit(int argc, char **argv, FILE *out, FILE *err) {
    if (!out) out = stdout;
    if (!err) err = stderr;

    const char *model_str = "gaussian";
    uint32_t poly_degree = 1;
    bool use_mle = false;
    bool use_unweighted = false;
    double confidence = 0.95;
    bool do_json = false;
    bool do_quiet = false;
    bool do_plot = false;
    const char *input_file = NULL;

    fit_bounds_ctx_t bctx;
    memset(&bctx, 0, sizeof(bctx));
    for (int i = 0; i < 16; ++i) {
        bctx.lower_bounds[i] = -INFINITY;
        bctx.upper_bounds[i] = INFINITY;
    }

    const cli_opt_spec_t specs[] = {
        {'m', "model", NULL, CLI_OPT_TYPE_STRING, &model_str, NULL, 0, "TYPE",
         "Model: gaussian (default), exponential, polynomial, breit-wigner, power-law, lognormal, gauss+linear, weibull, gamma, poisson, laplace", "gaussian"},
        {'d', "degree", NULL, CLI_OPT_TYPE_UINT32, &poly_degree, NULL, 0, "N",
         "Degree for polynomial model (default: 1, range: 0..10)", "1"},
        {0, "mle", NULL, CLI_OPT_TYPE_BOOL, &use_mle, NULL, 0, NULL,
         "Use Poisson Maximum Likelihood Estimation instead of Chi-Square", NULL},
        {0, "unweighted", NULL, CLI_OPT_TYPE_BOOL, &use_unweighted, NULL, 0, NULL,
         "Use Unweighted Least Squares", NULL},
        {'b', "bounds", NULL, CLI_OPT_TYPE_CALLBACK, &bctx, cb_parse_bounds, 0, "IDX=MIN:MAX",
         "Set parameter lower and upper bounds (e.g. --bounds=2=0.1:10.0)", NULL},
        {'f', "fix-param", NULL, CLI_OPT_TYPE_CALLBACK, &bctx, cb_parse_fix_param, 0, "IDX=VAL",
         "Freeze parameter to constant value (e.g. --fix-param=1=0.0)", NULL},
        {0, "range", NULL, CLI_OPT_TYPE_CALLBACK, &bctx, cb_parse_range, 0, "MIN:MAX",
         "Sub-range window for fitting [MIN, MAX]", NULL},
        {'c', "confidence", NULL, CLI_OPT_TYPE_DOUBLE, &confidence, NULL, 0, "LEV",
         "Confidence interval level (default: 0.95)", "0.95"},
        {'j', "json", NULL, CLI_OPT_TYPE_BOOL, &do_json, NULL, 0, NULL,
         "Emit structured JSON fit results", NULL},
        {'q', "quiet", NULL, CLI_OPT_TYPE_BOOL, &do_quiet, NULL, 0, NULL,
         "Output only optimal parameter values (tab-separated)", NULL},
        {'p', "plot", NULL, CLI_OPT_TYPE_BOOL, &do_plot, NULL, 0, NULL,
         "Render optional ASCII curve overlay above parameter table", NULL},
        {'i', "input", NULL, CLI_OPT_TYPE_STRING, &input_file, NULL, 0, "FILE",
         "Input histogram or raw sample stream file (default: stdin)", NULL}
    };

    cli_opt_parser_t parser;
    cli_opt_init(&parser, specs, sizeof(specs) / sizeof(specs[0]),
                 "histo-fit", "[OPTIONS] [HISTOGRAM_FILE...]",
                 "Fits parametric models to 1D histograms using non-linear least squares / Poisson MLE.");

    int rc = cli_opt_parse(&parser, argc, argv, 1);
    if (rc < 0) {
        cli_opt_print_help(&parser, out);
        cli_opt_free(&parser);
        return 0;
    }
    if (rc > 0) {
        fprintf(err, "%s\n", cli_opt_error(&parser));
        cli_opt_free(&parser);
        return 1;



( run in 0.880 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )