forked from abess-team/skscope
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUniversalData.cpp
203 lines (182 loc) · 6.89 KB
/
UniversalData.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/**
* author: Zezhi Wang
* Copyright (C) 2023 abess-team
* Licensed under the MIT License.
*/
#include "UniversalData.h"
#include "utilities.h"
#include <autodiff/forward/dual/eigen.hpp>
#include <iostream>
using namespace std;
using Eigen::Map;
using Eigen::Matrix;
UniversalData::UniversalData(Eigen::Index model_size, Eigen::Index sample_size, pybind11::object &data, UniversalModel *model, ConvexSolver convex_solver)
: model(model), convex_solver(convex_solver), sample_size(sample_size), model_size(model_size), effective_size(model_size)
{
this->effective_para_index = VectorXi::LinSpaced(model_size, 0, model_size - 1);
this->data = shared_ptr<pybind11::object>(new pybind11::object(data));
}
UniversalData UniversalData::slice_by_para(const VectorXi &target_para_index)
{
UniversalData tem(*this);
tem.effective_para_index = this->effective_para_index(target_para_index);
tem.effective_size = target_para_index.size();
return tem;
}
UniversalData UniversalData::slice_by_sample(const VectorXi &target_sample_index)
{
UniversalData tem(*this);
tem.sample_size = target_sample_index.size();
tem.data = shared_ptr<pybind11::object>(new pybind11::object(model->slice_by_sample(*data, target_sample_index)), model->deleter);
return tem;
}
Eigen::Index UniversalData::cols() const
{
return effective_size;
}
Eigen::Index UniversalData::rows() const
{
return sample_size;
}
const VectorXi &UniversalData::get_effective_para_index() const
{
return effective_para_index;
}
double UniversalData::loss(const VectorXd &effective_para)
{
VectorXd complete_para = VectorXd::Zero(this->model_size);
complete_para(this->effective_para_index) = effective_para;
return model->loss(complete_para, *this->data);
}
double UniversalData::loss_and_gradient(const VectorXd &effective_para, VectorXd &gradient)
{
double value = 0.0;
VectorXd complete_para = VectorXd::Zero(this->model_size);
complete_para(this->effective_para_index) = effective_para;
if (model->gradient_user_defined)
{
// Note: using complete_para to store gradient isn't a good idea, just for saving memory
tie(value, complete_para) = model->gradient_user_defined(complete_para, *this->data);
gradient = complete_para(this->effective_para_index);
}
else
{ // forward autodiff
dual value_dual;
VectorXdual effective_para_dual = effective_para;
auto func = [this, &complete_para](VectorXdual const &effective_para_dual)
{
VectorXdual complete_para_dual = complete_para;
complete_para_dual(this->effective_para_index) = effective_para_dual;
return this->model->gradient_autodiff(complete_para_dual, *this->data);
};
gradient = autodiff::gradient(func, wrt(effective_para_dual), at(effective_para_dual), value_dual);
value = val(value_dual);
}
return value;
}
void UniversalData::gradient_and_hessian(const VectorXd &effective_para, VectorXd &gradient, MatrixXd &hessian)
{
gradient.resize(this->effective_size);
hessian.resize(this->effective_size, this->effective_size);
VectorXd complete_para = VectorXd::Zero(this->model_size);
VectorXd complete_grad = VectorXd::Zero(this->model_size);
complete_para(this->effective_para_index) = effective_para;
if (model->hessian_user_defined)
{
double value = 0.0;
tie(value, complete_grad) = model->gradient_user_defined(complete_para, *this->data);
gradient = complete_grad(this->effective_para_index);
hessian = model->hessian_user_defined(complete_para, *this->data)(this->effective_para_index, this->effective_para_index);
}
else
{ // forward autodiff
dual2nd value_dual;
VectorXdual2nd gradient_dual;
VectorXdual2nd effective_para_dual = effective_para;
auto func = [this, &complete_para](VectorXdual2nd const &effective_para_dual)
{
VectorXdual2nd complete_para_dual = complete_para;
complete_para_dual(this->effective_para_index) = effective_para_dual;
return this->model->hessian_autodiff(complete_para_dual, *this->data);
};
hessian = autodiff::hessian(func, wrt(effective_para_dual), at(effective_para_dual), value_dual, gradient_dual);
for (Eigen::Index i = 0; i < this->effective_size; i++)
{
gradient[i] = val(gradient_dual[i]);
}
}
}
double UniversalData::optimize(VectorXd &effective_para)
{
if (effective_para.size() == 0)
{
return model->loss(VectorXd::Zero(this->model_size), *this->data);
}
auto value_and_grad = [this](const VectorXd &complete_para, pybind11::object data) -> pair<double, VectorXd>
{
if (this->model->gradient_user_defined)
{
return this->model->gradient_user_defined(complete_para, data);
}
else
{ // forward autodiff
dual value_dual;
VectorXdual complete_para_dual = complete_para;
VectorXd gradient = autodiff::gradient(this->model->gradient_autodiff, wrt(complete_para_dual), at(complete_para_dual, data), value_dual);
double value = val(value_dual);
return make_pair(value, gradient);
}
};
VectorXd complete_para = VectorXd::Zero(this->model_size);
complete_para(this->effective_para_index) = effective_para;
double loss;
tie(loss, complete_para) = this->convex_solver(
model->loss,
value_and_grad,
complete_para,
this->effective_para_index,
*this->data);
effective_para = complete_para(this->effective_para_index);
return loss;
}
void UniversalModel::set_loss_of_model(function<double(VectorXd const &, pybind11::object const &)> const &f)
{
loss = f;
}
void UniversalModel::set_gradient_autodiff(function<dual(VectorXdual const &, pybind11::object const &)> const &f)
{
gradient_autodiff = f;
gradient_user_defined = nullptr;
}
void UniversalModel::set_hessian_autodiff(function<dual2nd(VectorXdual2nd const &, pybind11::object const &)> const &f)
{
hessian_autodiff = f;
hessian_user_defined = nullptr;
}
void UniversalModel::set_gradient_user_defined(function<pair<double, VectorXd>(VectorXd const &, pybind11::object const &)> const &f)
{
gradient_user_defined = f;
gradient_autodiff = nullptr;
}
void UniversalModel::set_hessian_user_defined(function<MatrixXd(VectorXd const &, pybind11::object const &)> const &f)
{
hessian_user_defined = f;
hessian_autodiff = nullptr;
}
void UniversalModel::set_slice_by_sample(function<pybind11::object(pybind11::object const &, VectorXi const &)> const &f)
{
slice_by_sample = f;
}
void UniversalModel::set_deleter(function<void(pybind11::object const &)> const &f)
{
if (f)
{
deleter = [f](pybind11::object const *p)
{ f(*p); delete p; };
}
else
{
deleter = [](pybind11::object const *p)
{ delete p; };
}
}