From aefb7c936b95394eac87e973c3bea2ccd50b47f7 Mon Sep 17 00:00:00 2001 From: Nickolai Belakovski Date: Mon, 25 Mar 2024 10:27:28 +0800 Subject: [PATCH 1/2] Always initialize the result. This resolves #149. --- c/include/prima/prima.h | 16 +++++++++++----- c/prima.c | 19 +++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/c/include/prima/prima.h b/c/include/prima/prima.h index bc8ce3c729..6a041816d8 100644 --- a/c/include/prima/prima.h +++ b/c/include/prima/prima.h @@ -245,6 +245,9 @@ int prima_init_options(prima_options_t *const options); // Structure to hold the result +// prima_minimize will allocate the memory for x and nlconstr (if needed), +// and as such the user is expected to free the memory using prima_free_result +// once they are done using the contents of the result (or have saved the contents). typedef struct { // x: returned point @@ -277,11 +280,14 @@ int prima_free_result(prima_result_t *const result); /* - * The function that does the minimization using a PRIMA solver - * algorithm : optimization algorithm (see prima_algorithm) - * problem : optimization problem (see prima_problem) - * options : optimization options (see prima_options) - * result : optimization result (see prima_result) + * The function that does the minimization using a PRIMA solver. + * After using (or saving) the result, the user is expected to call + * prima_free_result, regardless of the return value of prima_minimize, + * in order to avoid memory leaks. + * algorithm : optimization algorithm (see prima_algorithm_t) + * problem : optimization problem (see prima_problem_t) + * options : optimization options (see prima_options_t) + * result : optimization result (see prima_result_t) * return : see prima_rc_t enum for return codes */ PRIMAC_API diff --git a/c/prima.c b/c/prima.c index 8cea9e7c78..53ce64b56b 100644 --- a/c/prima.c +++ b/c/prima.c @@ -98,7 +98,7 @@ int prima_init_result(prima_result_t *const result, const prima_problem_t proble if (!result->x) return PRIMA_MEMORY_ALLOCATION_FAILS; for (int i = 0; i < problem.n; i++) - result->x[i] = problem.x0[i]; + result->x[i] = NAN; // f: objective function value at the returned point result->f = NAN; @@ -238,12 +238,19 @@ int prima_minimize(const prima_algorithm_t algorithm, const prima_problem_t prob { int use_constr = (algorithm == PRIMA_COBYLA); - int info = prima_check_problem(problem, use_constr, algorithm); + int info = prima_init_result(result, problem); if (info == 0) - info = prima_init_result(result, problem); + info = prima_check_problem(problem, use_constr, algorithm); if (info == 0) { + // We copy x0 into result->x only after prima_check_problem has succeeded, + // so that if prima_check_problem failed, result->x will not contained a + // seemingly valid value. + for (int i = 0; i < problem.n; i++) { + result->x[i] = problem.x0[i]; + } + switch (algorithm) { case PRIMA_BOBYQA: bobyqa_c(problem.calfun, options.data, problem.n, result->x, &(result->f), problem.xl, problem.xu, &(result->nf), options.rhobeg, options.rhoend, options.ftarget, options.maxfun, options.npt, options.iprint, options.callback, &info); @@ -275,10 +282,10 @@ int prima_minimize(const prima_algorithm_t algorithm, const prima_problem_t prob default: return PRIMA_INVALID_INPUT; } - - result->status = info; - result->message = prima_get_rc_string(info); } + result->status = info; + result->message = prima_get_rc_string(info); + return info; } From 6460871a33f50b1586e385833617b8d2ee8fa017 Mon Sep 17 00:00:00 2001 From: Nickolai Belakovski Date: Mon, 25 Mar 2024 10:52:48 +0800 Subject: [PATCH 2/2] Make result consistent if memory allocation fails --- c/prima.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/c/prima.c b/c/prima.c index 53ce64b56b..ce16e276da 100644 --- a/c/prima.c +++ b/c/prima.c @@ -93,32 +93,12 @@ int prima_init_result(prima_result_t *const result, const prima_problem_t proble memset(result, 0, sizeof(prima_result_t)); - // x: returned point - result->x = (double*)malloc(problem.n * sizeof(double)); - if (!result->x) - return PRIMA_MEMORY_ALLOCATION_FAILS; - for (int i = 0; i < problem.n; i++) - result->x[i] = NAN; - // f: objective function value at the returned point result->f = NAN; // cstrv: constraint violation at the returned point (COBYLA and LINCOA only) result->cstrv = NAN; - // nlconstr: nonlinear constraint values at the returned point, of size m_nlcon (COBYLA only) - if (problem.m_nlcon <= 0) - result->nlconstr = NULL; - else { - result->nlconstr = (double*)malloc(problem.m_nlcon * sizeof(double)); - if (!result->nlconstr) { - free(result->x); - return PRIMA_MEMORY_ALLOCATION_FAILS; - } - for (int i = 0; i < problem.m_nlcon; i++) - result->nlconstr[i] = NAN; - } - // nf: number of function evaluations result->nf = INT_MIN; @@ -128,6 +108,22 @@ int prima_init_result(prima_result_t *const result, const prima_problem_t proble // message: exit message result->message = NULL; + // x: returned point + result->x = (double*)malloc(problem.n * sizeof(double)); + if (!result->x) + return PRIMA_MEMORY_ALLOCATION_FAILS; + for (int i = 0; i < problem.n; i++) + result->x[i] = NAN; + + // nlconstr: nonlinear constraint values at the returned point, of size m_nlcon (COBYLA only) + result->nlconstr = (double*)malloc(problem.m_nlcon * sizeof(double)); + if (!result->nlconstr) { + free(result->x); + return PRIMA_MEMORY_ALLOCATION_FAILS; + } + for (int i = 0; i < problem.m_nlcon; i++) + result->nlconstr[i] = NAN; + return 0; }