LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
optimize_lm.c
浏览该文件的文档.
1#include "optimize_internal.h"
2
8
14
15static lmmc_status_t lm_workspace_create(size_t n, lmmc_lm_workspace_t* work)
16{
17 lmmc_status_t status = lmmc_opt_workspace_create(n, 6, 3, 0, 1, &work->arena);
18 if (status != LMMC_STATUS_OK) { return status; }
19 work->residual = lmmc_opt_workspace_vector(&work->arena, n);
21 work->delta = lmmc_opt_workspace_vector(&work->arena, n);
22 work->gradient = lmmc_opt_workspace_vector(&work->arena, n);
23 work->trial = lmmc_opt_workspace_vector(&work->arena, n);
24 work->rhs = lmmc_opt_workspace_vector(&work->arena, n);
25 work->jacobian = lmmc_opt_workspace_matrix(&work->arena, n);
26 work->gram = lmmc_opt_workspace_matrix(&work->arena, n);
27 work->augmented = lmmc_opt_workspace_matrix(&work->arena, n);
28 return LMMC_STATUS_OK;
29}
30
31static lmmc_real_t lm_gradient(lmmc_lm_workspace_t* work)
32{
33 const size_t n = work->gradient.size;
34 size_t i, k;
35 for (i = 0; i < n; ++i) {
36 work->gradient.data[i] = 0.0;
37 for (k = 0; k < n; ++k) {
38 work->gradient.data[i] += work->jacobian.data[k * work->jacobian.stride + i] *
39 work->residual.data[k];
40 }
41 }
42 return lmmc_optimize_norm(&work->gradient);
43}
44
45static void lm_gram(lmmc_lm_workspace_t* work)
46{
47 const size_t n = work->gradient.size;
48 size_t i, j, k;
49 for (i = 0; i < n; ++i) {
50 for (j = 0; j < n; ++j) {
51 lmmc_real_t sum = 0.0;
52 for (k = 0; k < n; ++k) {
53 sum += work->jacobian.data[k * work->jacobian.stride + i] *
54 work->jacobian.data[k * work->jacobian.stride + j];
55 }
56 work->gram.data[i * work->gram.stride + j] = sum;
57 }
58 }
59}
60
61static lmmc_lm_trial_t lm_trial_direction(
62 lmmc_lm_workspace_t* work, lmmc_real_t lambda)
63{
64 const size_t n = work->gradient.size;
65 lmmc_status_t status;
66 size_t i;
67 memcpy(work->augmented.data, work->gram.data, n * n * sizeof(lmmc_real_t));
68 for (i = 0; i < n; ++i) {
69 work->augmented.data[i * work->augmented.stride + i] += lambda;
70 }
71 status = lmmc_lu_decompose_inplace(&work->augmented, work->arena.pivots, NULL);
72 if (status == LMMC_STATUS_SINGULAR_MATRIX) { return LMMC_LM_TRIAL_RETRY; }
73 if (status != LMMC_STATUS_OK) { return LMMC_LM_TRIAL_FAILURE; }
74 for (i = 0; i < n; ++i) {
75 work->rhs.data[i] = -work->gradient.data[i];
76 }
77 status = lmmc_lu_solve(&work->augmented, work->arena.pivots,
78 &work->rhs, &work->delta);
80}
81
82static int lm_step(
83 lmmc_lm_workspace_t* work, lmmc_opt_func_t residual, void* user_data,
84 lmmc_vec_t* x, lmmc_real_t* lambda, lmmc_real_t* norm)
85{
86 int attempt;
87 size_t i;
88 lm_gram(work);
89 for (attempt = 0; attempt < 20; ++attempt) {
90 lmmc_real_t next_norm;
91 lmmc_lm_trial_t trial = lm_trial_direction(work, *lambda);
92 if (trial == LMMC_LM_TRIAL_FAILURE) { return 0; }
93 if (trial == LMMC_LM_TRIAL_RETRY) {
94 *lambda *= 10.0;
95 continue;
96 }
97 for (i = 0; i < x->size; ++i) {
98 work->trial.data[i] = x->data[i] + work->delta.data[i];
99 }
100 if (residual(&work->trial, &work->next_residual, user_data) != LMMC_STATUS_OK) {
101 *lambda *= 10.0;
102 continue;
103 }
104 next_norm = lmmc_optimize_norm(&work->next_residual);
105 if (next_norm < *norm) {
106 memcpy(x->data, work->trial.data, x->size * sizeof(lmmc_real_t));
107 memcpy(work->residual.data, work->next_residual.data,
108 x->size * sizeof(lmmc_real_t));
109 *norm = next_norm;
110 *lambda *= 0.1;
111 if (*lambda < 1e-15) *lambda = 1e-15;
112 return 1;
113 }
114 *lambda *= 10.0;
115 if (*lambda > 1e15) { return 0; }
116 }
117 return 0;
118}
119
120static int lm_stop(lmmc_real_t norm, lmmc_real_t grad_norm,
121 lmmc_real_t initial_residual, lmmc_real_t* initial_gradient,
122 size_t iter, const lmmc_optimize_config_t* cfg, lmmc_optimize_result_t* out)
123{
124 if (!isfinite(grad_norm)) {
126 return 1;
127 }
128 if (iter == 0) *initial_gradient = grad_norm;
129 if (lmmc_optimize_has_converged(norm, initial_residual, cfg) ||
130 lmmc_optimize_has_converged(grad_norm, *initial_gradient, cfg)) {
131 out->converged = 1;
132 return 1;
133 }
134 if (iter == cfg->max_iter) {
136 return 1;
137 }
138 return 0;
139}
140
142 lmmc_opt_func_t residual, lmmc_opt_jac_t J, void* user_data,
143 lmmc_vec_t* x, const lmmc_optimize_config_t* cfg,
145{
147 lmmc_status_t status;
148 lmmc_real_t lambda, norm, initial_residual, initial_gradient = 0.0;
149 size_t iter;
150 if (residual == NULL || !lmmc_optimize_arguments_valid(x, cfg, out)) {
152 }
154 lambda = cfg->lm_damping;
155 status = lm_workspace_create(x->size, &work);
156 if (status != LMMC_STATUS_OK) { return status; }
157 if (residual(x, &work.residual, user_data) != LMMC_STATUS_OK) {
159 goto cleanup;
160 }
161 norm = lmmc_optimize_norm(&work.residual);
162 initial_residual = norm;
163 for (iter = 0; ; ++iter) {
164 lmmc_real_t grad_norm;
165 out->final_residual = norm;
166 if (!isfinite(norm)) {
168 break;
169 }
171 status = lmmc_optimize_jacobian(residual, J, user_data, x,
172 &work.residual, &work.jacobian, &work.trial, &work.next_residual);
173 if (status != LMMC_STATUS_OK) {
175 break;
176 }
177 grad_norm = lm_gradient(&work);
178 if (lm_stop(norm, grad_norm, initial_residual, &initial_gradient,
179 iter, cfg, out)) { break; }
180 if (!lm_step(&work, residual, user_data, x, &lambda, &norm)) {
182 break;
183 }
184 out->num_iter = iter + 1;
185 {
186 const lmmc_real_t values[] = {norm, lambda};
187 lmmc_optimize_emit(cfg, "levenberg_marquardt", iter + 1, values, 2);
188 }
189 }
190cleanup:
192 return LMMC_STATUS_OK;
193}
double lmmc_real_t
Public and internal LMMC real type.
lmmc_real_t * data
lmmc_real_t * data
行优先存储的稠密矩阵。
一维稠密向量。
lmmc_status_t lmmc_lu_solve(const lmmc_mat_t *lu, const size_t *pivots, const lmmc_vec_t *b, lmmc_vec_t *x)
利用 LU 分解结果求解线性系统 A*x = b。
lmmc_status_t lmmc_lu_decompose_inplace(lmmc_mat_t *a, size_t *pivots, size_t *out_swap_count)
就地 LU 分解(带部分主元选取):P*A = L*U。
void lmmc_memory_free(void *pointer)
lmmc_real_t lmmc_optimize_norm(const lmmc_vec_t *v)
计算向量的 L2 范数。
lmmc_mat_t lmmc_opt_workspace_matrix(lmmc_opt_workspace_t *work, size_t n)
lmmc_status_t lmmc_optimize_jacobian(lmmc_opt_func_t F, lmmc_opt_jac_t J, void *user_data, const lmmc_vec_t *x, const lmmc_vec_t *Fx, lmmc_mat_t *matrix, lmmc_vec_t *x_pert, lmmc_vec_t *F_pert)
临时向量由外层求解器一次性分配。
void lmmc_optimize_emit(const lmmc_optimize_config_t *cfg, const char *operation, size_t iteration, const lmmc_real_t *values, size_t value_count)
lmmc_vec_t lmmc_opt_workspace_vector(lmmc_opt_workspace_t *work, size_t n)
int lmmc_optimize_has_converged(lmmc_real_t residual, lmmc_real_t initial_residual, const lmmc_optimize_config_t *cfg)
lmmc_status_t lmmc_opt_workspace_create(size_t n, size_t vectors, size_t matrices, size_t extra, int need_pivots, lmmc_opt_workspace_t *work)
void lmmc_optimize_result_init(lmmc_optimize_result_t *out)
int lmmc_optimize_arguments_valid(const lmmc_vec_t *x, const lmmc_optimize_config_t *cfg, const lmmc_optimize_result_t *out)
@ LMMC_OPT_FAILURE_MAX_ITER
@ LMMC_OPT_FAILURE_NUMERICAL_ISSUE
lmmc_status_t(*) lmmc_opt_func_t(const lmmc_vec_t *x, lmmc_vec_t *F, void *user_data)
非线性函数回调:计算 F(x)。
lmmc_status_t lmmc_minimize_levenberg_marquardt(lmmc_opt_func_t residual, lmmc_opt_jac_t J, void *user_data, lmmc_vec_t *x, const lmmc_optimize_config_t *cfg, lmmc_optimize_result_t *out)
Levenberg-Marquardt 非线性最小二乘。
lmmc_optimize_failure_t failure_reason
lmmc_status_t(*) lmmc_opt_jac_t(const lmmc_vec_t *x, lmmc_mat_t *J, void *user_data)
Jacobian 回调:计算 J(x)。
优化算法配置参数。
工作区由本次调用拥有,视图仅借用存储,释放由调用方负责。
lmmc_opt_workspace_t arena
lmmc_lm_trial_t
@ LMMC_LM_TRIAL_RETRY
@ LMMC_LM_TRIAL_FAILURE
@ LMMC_LM_TRIAL_READY
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_OK
@ LMMC_STATUS_SINGULAR_MATRIX
@ LMMC_STATUS_INVALID_ARGUMENT