让A是:
class A {
std::vector<double> values_;
public:
A(const std::vector<double> &values) : values_(values){};
void bumpAt(std::size_t i, const double &df) {
values_[i] += df;
virtual method1();
virtual method2();
...
}
class B : public A {
overrides methods
...
}为了简单起见,请考虑以下函数:
double foo(input1, input2, ..., const A &a, const B &b, inputK, ...) {
/* do complex stuff */
return ...;
}我们希望对foo()的论点加以区分。因此,一阶灵敏度d foo/d a是一个大小等于a.size()的std::vector<double>。同样的道理也适用于d foo/d b。
简单的实现如下:
// compute d foo/d a
std::vector<double> computeDfDa(input1, input2, ..., const A &a, const B &b, inputK, ..., double da = 1.0){
std::vector<double> dfda = {};
auto aUp = a.copy();
auto aDown = a.copy();
for (auto i = 0; i < a.size(); ++i) {
// bump up
aUp.bumpAt(i, da);
// bump down
aDown.bumpAt(i, -da);
auto up = foo(input1, input2, ..., aUp, b, inputK, ...);
auto down = foo(input1, input2, ..., aDown, b, inputK, ...);
auto derivative = (up - down) / 2.0 / da;
dfda.pushback(derivative);
// revert bumps
aUp.bumpAt(i, -da);
aDown.bumpAt(i, da);
}
return dfda;
}
// compute d foo/d b
std::vector<double> computeDfDb(input1, input2, ..., const A &a, const B &b, inputK, ..., double db = 0.01){
std::vector<double> dfdb = {};
auto bUp = b.copy();
auto bDown = b.copy();
for (auto i = 0; i < a.size(); ++i) {
// bump up
bUp.bumpAt(i, db);
// bump down
bDown.bumpAt(i, -db);
auto up = foo(input1, input2, ..., a, bUp, inputK, ...);
auto down = foo(input1, input2, ..., a, bDown, inputK, ...);
auto derivative = (up - down) / 2.0 / db;
dfdb.pushback(derivative);
// revert bumps
bUp.bumpAt(i, -db);
bDown.bumpAt(i, db);
}
return dfdb;
}但是,对于computeDfDa()和computeDfDb(),我们的代码基本上是相同的。
是否有任何设计模式允许有一个独特的(可能是模板化的)函数,可以自动理解哪个输入可以凸点?
请注意,a和b在输入中的位置不是可交换的。
如果foo()的复杂性和输入数量更大,天真的解决方案将生成大量无用的代码,因为我们必须为每个foo()输入x编写一个computeDfDx()函数。
发布于 2020-08-26 05:34:07
由于compute顺序相同,但迭代循环通过不同的容器,因此可以重构此函数。
std::vector<double> computeLoop( std::vector<double> &v, std::vector<double> const &computeArg1, std::vector<double> const &computeArg2, double d = 1.0 )
{
std::vector<double> dfd = {};
for (auto i = 0; i < v.size(); ++i) {
// bump up
v[i] += d;
auto up = compute(computeArg1, computeArg2);
v[i] -= d;
// bump down
v[i] -= d;
auto down = compute(computeArg1, computeArg2);
v[i] += d;
auto derivative = (up - down) / 2.0 / d;
dfd.pushback(derivative);
}
}真正的电话。
auto dfda = computeLoop( a, a, b );
auto dfdb = computeLoop( b, a, b );让v通过引用传递,但它可能会导致维护问题。因为v可能是与computeArg1或computeArg2相同的引用,但是在computeLoop中这一点并不明显。将来可能有人会无意中违反了代码。
发布于 2020-08-26 07:15:04
问题是,抽象的层次发生了变化。
那些A,B类要么是..。
在第(1)种情况下,应该可以用类似的形式重写:
#include <... the usual suspects ... >
using VecF64 = std::vector<double>;
using VecVecF64 = std::vector<VecF64>;
// foo, dropping allusions of encapsulations. It KNOWS those A, B are vectors.
// Hence we can write it without knowledge of A, B types.
double foo(const VecVecF64& args) {
return <result-of-complicated-computations>;
}
// With that, it is also easier to write the differentiation function
VecVecF64 fooGradients( const VecVecF64& args, double delta = 0.01) {
VecVecF64 result;
result.reserve(args.size());
// for each arg vector in args, do your thing.
// ..
return result;
}如果(2),如果你对A,B的性质都是保密的,你怎么知道双数,foo可以用来计算它呢?这就引出了A的梯度向量的大小问题,使得这个凸点的想法不可能实现。
我猜,你遇到了一个问题。
https://stackoverflow.com/questions/63590783
复制相似问题