22 #include "CollectiveVariable.h"
23 #include "Validator/ObjectRequirement.h"
24 #include "Drivers/DriverException.h"
46 double scaling_factor_;
47 std::vector<unsigned int> num_nodes_;
48 std::vector<Eigen::MatrixXd> weight_coeff_;
49 std::vector<Vector> bias_;
50 std::vector<std::string> activations_;
68 double scaling_factor,
69 std::vector<unsigned int> num_nodes,
70 std::string coeff_file,
71 std::vector<std::string> activations,
74 atomids_(atomids), scaling_factor_(scaling_factor), num_nodes_(num_nodes),
75 activations_(activations), out_index_(out_index)
78 std::ifstream my_f(coeff_file);
81 if (num_nodes_[0] != atomids_.size() * 3) {
83 "WARNING: input dim should be " + std::to_string(atomids_.size() * 3) +
" found: "
84 + std::to_string(num_nodes_[0])
87 while (std::getline(my_f, temp_vec) )
89 std::istringstream ss(temp_vec);
91 std::vector<double> temp_weight, temp_bias;
92 while (std::getline(ss, token,
','))
94 temp_weight.push_back(stod(token));
96 if (temp_weight.size() != num_nodes_[layer_index] * num_nodes_[layer_index + 1]) {
98 "WARNING: layer weight size = " + std::to_string(temp_weight.size()) +
" expected: "
99 + std::to_string(num_nodes_[layer_index] * num_nodes_[layer_index + 1])
102 Eigen::Map<Eigen::MatrixXd> temp_weight_v(
103 &temp_weight[0], num_nodes_[layer_index], num_nodes_[layer_index + 1]
105 std::getline(my_f, temp_vec);
106 std::istringstream ss2(temp_vec);
107 while (std::getline(ss2, token,
','))
109 temp_bias.push_back(stod(token));
111 if (temp_bias.size() != num_nodes_[layer_index + 1]) {
113 "WARNING: layer bias size = " + std::to_string(temp_bias.size())
114 +
" expected: " + std::to_string(num_nodes_[layer_index + 1])
117 Vector temp_bias_v = Vector::Map(temp_bias.data(), temp_bias.size());
118 weight_coeff_.push_back(temp_weight_v);
119 bias_.push_back(temp_bias_v);
130 using std::to_string;
132 std::vector<int> found;
134 size_t nfound = found.size();
135 MPI_Allreduce(MPI_IN_PLACE, &nfound, 1, MPI_INT, MPI_SUM, snapshot.
GetCommunicator());
137 if(nfound != atomids_.size())
139 "ANNCV: Expected to find " +
140 to_string(atomids_.size()) +
141 " atoms, but only found " +
142 to_string(nfound) +
"."
146 std::vector<Vector> forward_prop(
Vector& input_vec) {
147 std::vector<Vector> output_of_layers;
148 Vector temp_out = input_vec;
149 output_of_layers.push_back(temp_out);
150 for (
size_t ii = 0; ii < weight_coeff_.size(); ii ++) {
151 temp_out = weight_coeff_[ii].transpose() * temp_out + bias_[ii];
152 if (activations_[ii] ==
"Tanh") {
153 for (
int kk = 0; kk < temp_out.size(); kk ++) {
154 temp_out[kk] = std::tanh(temp_out[kk]);
157 else if (activations_[ii] ==
"ReLU") {
158 for (
int kk = 0; kk < temp_out.size(); kk ++) {
159 temp_out[kk] = temp_out[kk] < 0 ? 0 : temp_out[kk];
162 output_of_layers.push_back(temp_out);
164 return output_of_layers;
167 std::vector<Vector> back_prop(std::vector<Vector>& output_of_layers) {
168 auto deriv_back = output_of_layers;
169 int num = output_of_layers.size();
170 for (
int ii = 0; ii < output_of_layers[num - 1].size(); ii ++ ) {
171 if (ii == out_index_) {
172 deriv_back[num - 1][ii] = 1;
175 deriv_back[num - 1][ii] = 0;
178 for (
int ii = num - 2; ii >= 0; ii --) {
179 if (activations_[ii] ==
"Tanh") {
180 for (
int kk = 0; kk < deriv_back[ii + 1].size(); kk ++) {
181 deriv_back[ii + 1][kk] = deriv_back[ii + 1][kk] * (
182 1 - output_of_layers[ii + 1][kk] * output_of_layers[ii + 1][kk]);
185 deriv_back[ii] = weight_coeff_[ii] * deriv_back[ii + 1];
206 std::vector<Vector3> positions(atomids_.size(),
Vector3({0,0,0}));
209 for (
size_t ii = 0; ii < atomids_.size(); ii ++) {
210 if (local_idx[ii] != -1) {
211 positions[ii] = pos[local_idx[ii]];
216 MPI_Allreduce(MPI_IN_PLACE, positions.data(), positions.size(), MPI_DOUBLE, MPI_SUM, comm);
219 for (
auto& temp_pos: positions) {
220 temp_pos = temp_pos - com;
222 Vector input_vec(positions.size() * 3);
223 for (
size_t ii = 0; ii < positions.size(); ii ++) {
224 for (
size_t jj = 0; jj < 3; jj ++) {
225 input_vec[ii * 3 + jj] = positions[ii][jj];
228 input_vec = input_vec / scaling_factor_;
229 auto output_of_layers = forward_prop(input_vec);
230 val_ = output_of_layers[output_of_layers.size() - 1][out_index_];
231 auto deriv_back = back_prop(output_of_layers);
233 int num_atoms = deriv_back[0].size() / 3;
234 double average[3] = {0};
235 for (
int kk = 0; kk < 3; kk ++) {
236 for (
int ii = 0; ii < num_atoms; ii ++) {
237 average[kk] += deriv_back[0][ii * 3 + kk];
239 average[kk] /= num_atoms;
244 static ANNCV*
Build(
const Json::Value& json,
const std::string& path)
248 Json::CharReaderBuilder rbuilder;
249 Json::CharReader* reader = rbuilder.newCharReader();
251 reader->parse(JsonSchema::ANNCV.c_str(),
252 JsonSchema::ANNCV.c_str() + JsonSchema::ANNCV.size(),
254 validator.
Parse(schema, path);
261 std::vector<int> atomids;
262 for(
auto& s : json[
"atom_ids"])
263 atomids.push_back(s.asInt());
264 double scaling_factor = json[
"scaling_factor"].asDouble();
265 std::vector<unsigned int> num_nodes;
266 for (
auto &s : json[
"num_nodes"])
267 num_nodes.push_back(s.asUInt());
268 std::vector<std::string> activations;
269 std::string coeff_file = json[
"coeff_file"].asString();
270 for (
auto &s : json[
"activations"]) {
271 auto name = s.asString();
272 if (name ==
"Tanh" || name ==
"ReLU" || name ==
"Linear")
273 activations.push_back(name);
275 throw std::runtime_error(
"invalid activation function " + name +
" provided");
277 int out_index = json[
"index"].asInt();
278 return new ANNCV(atomids, scaling_factor, num_nodes, coeff_file, activations, out_index);
Requirements on an object.
virtual void Parse(Value json, const std::string &path) override
Parse JSON value to generate Requirement(s).
virtual void Validate(const Value &json, const std::string &path) override
Validate JSON value.
std::vector< std::string > GetErrors()
Get list of error messages.
bool HasErrors()
Check if errors have occured.
ANN (artifical neural network) collective variables.
void Initialize(const Snapshot &snapshot) override
Initialize necessary variables.
void Evaluate(const Snapshot &snapshot) override
Evaluate the CV.
static ANNCV * Build(const Json::Value &json, const std::string &path)
Set up collective variable.
ANNCV(Label atomids, double scaling_factor, std::vector< unsigned int > num_nodes, std::string coeff_file, std::vector< std::string > activations, int out_index)
Constructor.
Exception to be thrown when building the Driver fails.
Abstract class for a collective variable.
std::vector< Vector3 > grad_
Gradient vector dCv/dxi.
double val_
Current value of CV.
Class containing a snapshot of the current simulation in time.
unsigned GetNumAtoms() const
Get number of atoms in this snapshot.
void GetLocalIndices(const Label &ids, Label *indices) const
const mxx::comm & GetCommunicator() const
Get communicator for walker.
const std::vector< Vector3 > & GetPositions() const
Access the particle positions.
Vector3 CenterOfMass(const Label &indices, bool mass_weight=true) const
Compute center of mass of a group of atoms based on index.
Eigen::VectorXd Vector
Arbitrary length vector.
Eigen::Vector3d Vector3
Three-dimensional vector.
std::vector< int > Label
List of integers.