LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
optimize_lbfgs.c
浏览该文件的文档.
1#include "optimize_internal.h"
2
9
10static lmmc_status_t lbfgs_workspace_create(
11 size_t n, size_t m, lmmc_lbfgs_workspace_t* work)
12{
13 size_t mn, pairs, scalars, extra;
14 lmmc_status_t status;
15 if (!lmmc_safe_mul_size(m, n, &mn) ||
16 !lmmc_safe_mul_size(mn, 2, &pairs) ||
17 !lmmc_safe_mul_size(m, 2, &scalars) ||
18 !lmmc_safe_add_size(pairs, scalars, &extra)) {
20 }
21 status = lmmc_opt_workspace_create(n, 4, 0, extra, 0, &work->arena);
22 if (status != LMMC_STATUS_OK) { return LMMC_STATUS_ALLOCATION_FAILED; }
23 work->g = lmmc_opt_workspace_vector(&work->arena, n);
24 work->q = lmmc_opt_workspace_vector(&work->arena, n);
25 work->x_new = lmmc_opt_workspace_vector(&work->arena, n);
26 work->g_new = lmmc_opt_workspace_vector(&work->arena, n);
27 work->alpha = lmmc_opt_workspace_vector(&work->arena, m);
28 work->rho = lmmc_opt_workspace_vector(&work->arena, m);
29 work->s = (lmmc_mat_t){m, n, n, work->arena.next, 0};
30 work->arena.next += mn;
31 work->y = (lmmc_mat_t){m, n, n, work->arena.next, 0};
32 work->history_count = 0;
33 work->oldest = 0;
34 return LMMC_STATUS_OK;
35}
36
37static void lbfgs_scale_direction(lmmc_lbfgs_workspace_t* work)
38{
39 const size_t n = work->g.size;
40 size_t i, newest;
41 lmmc_real_t sy = 0.0, yy = 0.0;
42 if (work->history_count == 0) return;
43 newest = (work->oldest + work->history_count - 1) % work->s.rows;
44 for (i = 0; i < n; ++i) {
45 sy += work->s.data[newest * n + i] * work->y.data[newest * n + i];
46 yy += work->y.data[newest * n + i] * work->y.data[newest * n + i];
47 }
48 if (yy > 1e-300) {
49 const lmmc_real_t gamma = sy / yy;
50 for (i = 0; i < n; ++i) {
51 work->q.data[i] *= gamma;
52 }
53 }
54}
55
57static void lbfgs_direction(lmmc_lbfgs_workspace_t* work)
58{
59 const size_t n = work->g.size;
60 const size_t m = work->s.rows;
61 const size_t bound = work->history_count;
62 size_t k, i;
63 memcpy(work->q.data, work->g.data, n * sizeof(lmmc_real_t));
64 for (k = 0; k < bound; ++k) {
65 const size_t idx = (work->oldest + work->history_count - 1 - k) % m;
66 lmmc_real_t dot = 0.0;
67 for (i = 0; i < n; ++i) {
68 dot += work->s.data[idx * n + i] * work->q.data[i];
69 }
70 work->alpha.data[k] = work->rho.data[idx] * dot;
71 for (i = 0; i < n; ++i) {
72 work->q.data[i] -= work->alpha.data[k] * work->y.data[idx * n + i];
73 }
74 }
75 lbfgs_scale_direction(work);
76 for (k = bound; k-- > 0;) {
77 const size_t idx = (work->oldest + work->history_count - 1 - k) % m;
78 lmmc_real_t dot = 0.0;
79 lmmc_real_t beta;
80 for (i = 0; i < n; ++i) {
81 dot += work->y.data[idx * n + i] * work->q.data[i];
82 }
83 beta = work->rho.data[idx] * dot;
84 for (i = 0; i < n; ++i) {
85 work->q.data[i] += (work->alpha.data[k] - beta) * work->s.data[idx * n + i];
86 }
87 }
88 for (i = 0; i < n; ++i) {
89 work->q.data[i] = -work->q.data[i];
90 }
91}
92
93static void lbfgs_store_history(
94 lmmc_lbfgs_workspace_t* work, const lmmc_vec_t* x)
95{
96 const size_t n = x->size;
97 const size_t m = work->s.rows;
98 size_t store_idx, i;
99 lmmc_real_t sy = 0.0;
100 if (work->history_count < m) {
101 store_idx = (work->oldest + work->history_count) % m;
102 work->history_count++;
103 } else {
104 store_idx = work->oldest;
105 work->oldest = (work->oldest + 1) % m;
106 }
107 for (i = 0; i < n; ++i) {
108 work->s.data[store_idx * n + i] = work->x_new.data[i] - x->data[i];
109 work->y.data[store_idx * n + i] = work->g_new.data[i] - work->g.data[i];
110 sy += work->s.data[store_idx * n + i] * work->y.data[store_idx * n + i];
111 }
112 if (fabs(sy) > 1e-300) {
113 work->rho.data[store_idx] = 1.0 / sy;
114 } else {
115 work->rho.data[store_idx] = 0.0;
116 }
117}
118
119static lmmc_real_t lbfgs_descent_derivative(
121{
122 size_t i;
123 lmmc_real_t dg = 0.0;
124 for (i = 0; i < work->g.size; ++i) {
125 dg += work->g.data[i] * work->q.data[i];
126 }
127 if (dg >= 0.0) {
128 for (i = 0; i < work->g.size; ++i) {
129 work->q.data[i] = -work->g.data[i];
130 }
131 dg = -norm * norm;
132 }
133 return dg;
134}
135
136static lmmc_optimize_failure_t lbfgs_step(
138 lmmc_opt_grad_t grad, void* user_data, lmmc_vec_t* x,
139 lmmc_real_t* value, lmmc_real_t norm)
140{
141 lmmc_real_t dg;
142 lbfgs_direction(work);
143 dg = lbfgs_descent_derivative(work, norm);
144 if (!lmmc_optimize_armijo(obj, user_data, x, &work->q,
145 &work->x_new, value, dg, 40, 0)) {
147 }
148 if (grad(&work->x_new, &work->g_new, user_data) != LMMC_STATUS_OK) {
150 }
151 lbfgs_store_history(work, x);
152 memcpy(x->data, work->x_new.data, x->size * sizeof(lmmc_real_t));
153 memcpy(work->g.data, work->g_new.data, x->size * sizeof(lmmc_real_t));
155}
156
158 lmmc_opt_obj_t obj, lmmc_opt_grad_t grad, void* user_data,
159 lmmc_vec_t* x, const lmmc_optimize_config_t* cfg,
161{
163 lmmc_status_t status;
164 lmmc_real_t value, initial_residual = 0.0;
165 size_t iter;
166 if (obj == NULL || grad == NULL ||
167 !lmmc_optimize_arguments_valid(x, cfg, out)) {
169 }
171 status = lbfgs_workspace_create(x->size, cfg->lbfgs_memory, &work);
172 if (status != LMMC_STATUS_OK) { return status; }
173 if (grad(x, &work.g, user_data) != LMMC_STATUS_OK) {
175 goto cleanup;
176 }
177 value = obj(x, user_data);
178 for (iter = 0; ; ++iter) {
179 const lmmc_real_t norm = lmmc_optimize_norm(&work.g);
180 out->final_residual = norm;
181 if (!isfinite(value)) {
183 break;
184 }
185 if (lmmc_optimize_stop(norm, &initial_residual, iter, cfg, out)) { break; }
186 out->failure_reason = lbfgs_step(&work, obj, grad, user_data, x, &value, norm);
187 if (out->failure_reason != LMMC_OPT_FAILURE_NONE) { break; }
188 out->num_iter = iter + 1;
189 {
190 const lmmc_real_t values[] = {value, norm};
191 lmmc_optimize_emit(cfg, "lbfgs", iter + 1, values, 2);
192 }
193 }
194cleanup:
196 return LMMC_STATUS_OK;
197}
double lmmc_real_t
Public and internal LMMC real type.
lmmc_real_t * data
lmmc_real_t * data
行优先存储的稠密矩阵。
一维稠密向量。
void lmmc_memory_free(void *pointer)
int lmmc_optimize_armijo(lmmc_opt_obj_t obj, void *user_data, const lmmc_vec_t *x, const lmmc_vec_t *direction, lmmc_vec_t *trial, lmmc_real_t *value, lmmc_real_t derivative, int attempts, int subtract)
lmmc_real_t lmmc_optimize_norm(const lmmc_vec_t *v)
计算向量的 L2 范数。
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_stop(lmmc_real_t norm, lmmc_real_t *initial, size_t iteration, const lmmc_optimize_config_t *cfg, lmmc_optimize_result_t *out)
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_optimize_failure_t
优化失败原因枚举。
@ LMMC_OPT_FAILURE_NONE
@ LMMC_OPT_FAILURE_LINE_SEARCH_FAILED
@ LMMC_OPT_FAILURE_NUMERICAL_ISSUE
lmmc_status_t lmmc_minimize_lbfgs(lmmc_opt_obj_t obj, lmmc_opt_grad_t grad, void *user_data, lmmc_vec_t *x, const lmmc_optimize_config_t *cfg, lmmc_optimize_result_t *out)
L-BFGS 无约束最小化。
lmmc_optimize_failure_t failure_reason
lmmc_status_t(*) lmmc_opt_grad_t(const lmmc_vec_t *x, lmmc_vec_t *grad, void *user_data)
梯度回调:计算 ∇f(x)。
lmmc_real_t(*) lmmc_opt_obj_t(const lmmc_vec_t *x, void *user_data)
目标函数回调:计算标量 f(x)。
优化算法配置参数。
工作区由本次调用拥有,视图仅借用存储,释放由调用方负责。
lmmc_opt_workspace_t arena
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_OK
@ LMMC_STATUS_ALLOCATION_FAILED
@ LMMC_STATUS_INVALID_ARGUMENT