LA_library/regsurf.cc

1527 lines
52 KiB
C++

/*
LA: linear algebra C++ interface library
Kernel ridge regression module Copyright (C) 2024
Pavel Florian <florian43@seznam.cz> and Jiri Pittner <jiri.pittner@jh-inst.cas.cz> or <jiri@pittnerovi.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "regsurf.h"
namespace LA {
template <typename T>
unsigned int Build_s_RE_descriptor_mat(const unsigned int count_particles,
const unsigned int count_geometries, const T* distances, T* desc, const T* reference_geometry)
{
unsigned int i, j, k;
T distance;
T distance_ref;
// the RE descriptor
for (i = 0; i < count_geometries; i++)
{
// calculate upper part of descriptor matrix
for (j = 0; j < count_particles; j++)
for (k = j + 1; k < count_particles; k++)
{
distance = distances[(i * count_particles * count_particles) + (j * count_particles) + k];
distance_ref = reference_geometry[(j * count_particles) + k];
desc[(i * count_particles * count_particles) + (j * count_particles) + k] = distance_ref/distance;
}
// copy 0 to diagonal
for (j = 0; j < count_particles; j++)
desc[(i * count_particles * count_particles) + j * (count_particles + 1)] = 0;
// copy upper part of descriptor matrix under diagonal
for (j = 0; j < count_particles; j++)
for (k = 0; k < j; k++)
desc[(i * count_particles * count_particles) + (j * count_particles) + k] =
desc[(i * count_particles * count_particles) + (k * count_particles) + j];
}
return(0);
}
template <typename T>
unsigned int Build_s_coulomb_matrix_descriptor_mat(const unsigned int count_particles,
const unsigned int count_geometries, const T* distances, T* desc, const unsigned int* Z)
{
unsigned int i, j, k;
T distance;
// the coulomb matrix descriptor
for (i = 0; i < count_geometries; i++)
{
// calculate upper part of descriptor matrix
for (j = 0; j < count_particles; j++)
for (k = j + 1; k < count_particles; k++)
{
distance = distances[(i * count_particles * count_particles) + (j * count_particles) + k];
desc[(i * count_particles * count_particles) + (j * count_particles) + k] = (Z[j] * Z[k])/distance;
}
// calculate the diagonal
for (j = 0; j < count_particles; j++)
desc[(i * count_particles * count_particles) + j * (count_particles + 1)] = std::pow(Z[j], 2.4);
// copy upper part of descriptor matrix under diagonal
for (j = 0; j < count_particles; j++)
for (k = 0; k < j; k++)
desc[(i * count_particles * count_particles) + (j * count_particles) + k] =
desc[(i * count_particles * count_particles) + (k * count_particles) + j];
}
return(0);
}
template <typename T>
unsigned int Build_s_inverse_pairs_descriptor_mat(const unsigned int count_particles,
const unsigned int count_geometries, const T* distances, T* desc)
{
unsigned int i, j, k;
T distance;
// the inverse pairs descriptor
for (i = 0; i < count_geometries; i++)
{
// calculate upper part of descriptor matrix
for (j = 0; j < count_particles; j++)
for (k = 0; k < count_particles; k++)
{
distance = distances[(i * count_particles * count_particles) + (j * count_particles) + k];
desc[(i * count_particles * count_particles) + (j * count_particles) + k] = 1.00/distance;
}
// copy 0 to diagonal
for (j = 0; j < count_particles; j++)
desc[(i * count_particles * count_particles) + j * (count_particles + 1)] = 0;
// copy upper part of descriptor matrix under diagonal
for (j = 0; j < count_particles; j++)
for (k = 0; k < j; k++)
desc[(i * count_particles * count_particles) + (j * count_particles) + k] =
desc[(i * count_particles * count_particles) + (k * count_particles) + j];
}
return(0);
}
// The kernel regression of surfaces of geometries in in 1D, 2D, 3D and multi-D space
// and computing of ML-NEA absorption spectra
template <typename T>
REGSURF<T>::REGSURF(const unsigned int count_dimensions, const unsigned int count_kernels, const unsigned int count_geometries,
const unsigned int count_levels, const string descriptor, const string kernel, const T* params, const T* geometries,
const T* values, const unsigned int* Z)
{
Init(count_dimensions, count_kernels, count_geometries, count_levels, descriptor, kernel, params, geometries, values, Z);
}
template <typename T>
REGSURF<T>::REGSURF(const string geometries_file, const string y_file, const string param_file, const string Z_file)
{
unsigned int i, j, k, l;
unsigned int line_size, numbers_size;
ifstream geomfile(geometries_file);
ifstream yfile(y_file);
ifstream paramfile(param_file);
ifstream Zfile(Z_file);
string line;
T line_value;
// parameters of model
unsigned int count_dimensions;
unsigned int count_kernels;
unsigned int count_geometries;
unsigned int count_levels;
string kernel;
string descriptor;
const string numbers = "+-0123456789.,";
string readed_number;
bool read_switch;
bool decimal_switch;
// loading parameters for regression model from parameters_file
if (paramfile.is_open())
{
// load fixed paraeters of model
if (getline(paramfile, line))
count_dimensions = stoi(line);
else
{
laerror("file with model paramaters not valid");
return;
}
if (getline(paramfile, line))
count_kernels = stoi(line);
else
{
laerror("file with model paramaters not valid");
return;
}
if (getline(paramfile, line))
count_geometries = stoi(line);
else
{
laerror("file with model paramaters not valid");
return;
}
if (getline(paramfile, line))
count_levels = stoi(line);
else
{
laerror("file with model paramaters not valid");
return;
}
// load flexible parameters of model
if (getline(paramfile, line))
descriptor = line;
else
{
laerror("file with model paramaters not valid");
return;
}
if (getline(paramfile, line))
kernel = line;
else
{
laerror("file with model paramaters not valid");
return;
}
// alloacate memory for parameters file
{
s_param_file = new T[count_levels * 2 + 2];
if(!s_param_file) laerror("not allocated memory for parameters");
}
for (i = 0; i < ((2 * count_levels) + 1); i++)
{
if (getline(paramfile, line))
s_param_file[i] = stod(line);
else
{
laerror("file with model paramaters not valid");
return;
}
}
if (kernel == "Matern")
{
if (getline(paramfile, line))
s_param_file[(2 * count_levels) + 1] = static_cast<unsigned int>(stod(line) + 0.5);
else
{
laerror("file with model paramaters not valid");
return;
}
}
paramfile.close();
}
// alocating memory for y data for regression model from files
{
s_geom_file = new T[count_dimensions * count_kernels * count_geometries];
if(!s_geom_file) laerror("allocation error of s_geom_file");
s_y_file = new T[count_geometries * count_levels];
if(!s_y_file) laerror("allocation error of s_y_file");
if (descriptor == "Coulomb_matrix" or Z_file != "")
{
s_Z_file = new unsigned int[count_kernels];
if(!s_Z_file) laerror("allocation error of s_Z_file");
}
else s_Z_file=NULL;
s_y_min = new unsigned int[count_levels];
if(!s_y_min) laerror("allocation error of s_y_min");
}
// loading geometrical data for regression model from file
l = 0;
numbers_size = numbers.size();
if (geomfile.is_open())
{
for (i = 0; i < 2 * count_dimensions * count_kernels * count_geometries; i++)
{
if (getline(geomfile, line))
{
read_switch = false;
decimal_switch = false;
readed_number = "";
line_size = line.size();
for (j = 0; j < line_size; j++)
{
read_switch = false;
for (k = 0; k < numbers_size; k++)
{
if (line[j] == numbers[k])
{
readed_number.append(line, j, 1);
read_switch = true;
if (line[j] == numbers[12] or line[j] == numbers[13])
decimal_switch = true;
}
}
if (read_switch == false)
{
if (decimal_switch == true and readed_number != "")
{
line_value = stod(readed_number);
s_geom_file[l] = line_value;
l++;
}
readed_number = "";
read_switch = false;
decimal_switch = false;
}
}
if (decimal_switch == true and readed_number != "")
{
line_value = stod(readed_number);
s_geom_file[l] = line_value;
l++;
}
}
if (l == count_dimensions * count_kernels * count_geometries)
break;
}
geomfile.close();
}
else
{
laerror("file with geometries not open");
return;
}
if (l < count_dimensions * count_kernels * count_geometries)
{
laerror("incomplete file with geometries");
return;
}
// loading y data for model from file
if (yfile.is_open())
{
for (i = 0; i < count_geometries * count_levels; i++)
{
if (getline(yfile, line))
{
line_value = stod(line);
s_y_file[i] = line_value;
}
else
{
yfile.close();
laerror("incomplete file with y values");
return;
}
}
}
else
{
laerror("file with y values not open");
return;
}
// loading Z proton numbers of atoms
if (s_Z_file != nullptr and Z_file != "")
{
if (Zfile.is_open())
{
for (i = 0; i < count_kernels; i++)
{
if (getline(Zfile, line))
s_Z_file[i] = stoi(line);
else
{
laerror("incomplete file with Z quantum numbers ");
return;
}
}
Zfile.close();
}
else
{
laerror("file with Z quantum numbers not open");
return;
}
}
// callling init function with loaded parameters, y and geometries data
Init(count_dimensions, count_kernels, count_geometries, count_levels, descriptor, kernel, s_param_file, s_geom_file, s_y_file,
s_Z_file);
}
template <typename T>
void REGSURF<T>::Init(const unsigned int count_dimensions, const unsigned int count_kernels, const unsigned int count_geometries,
const unsigned int count_levels, const string descriptor, const string kernel, const T* params, const T* geometries,
const T* values, const unsigned int* Z)
{
unsigned int i, j;
T min_value;
s_kernel_type = kernel;
if (kernel != "Gaussian" and kernel != "Laplacian" and kernel != "Exponential" and kernel != "Matern")
{
laerror("invalid type of kernel");
return;
}
if (kernel == "Gaussian")
s_kernel_id = 0;
if (kernel == "Laplacian")
s_kernel_id = 1;
if (kernel == "Exponential")
s_kernel_id = 2;
if (kernel == "Matern")
s_kernel_id = 3;
s_descriptor_type = descriptor;
if (descriptor == "RE")
s_descriptor_id = 0;
if (descriptor == "Coulomb_matrix")
s_descriptor_id = 1;
if (descriptor == "Inverse_pairs")
s_descriptor_id = 2;
s_count_kernels = count_kernels;
s_count_dimensions = count_dimensions;
s_count_geometries = count_geometries;
s_count_levels = count_levels;
// copy parameters and data
{
s_lambda = new T[count_levels];
s_alpha = new T[count_levels];
if(!s_lambda || !s_alpha) laerror("allocation error in REGSURF::Init");
}
for (i = 0; i < count_levels; i++)
s_lambda[i] = params[i];
for (i = count_levels; i < (2 * count_levels); i++)
{
s_alpha[i - count_levels] = params[i];
if (s_alpha[i] > 1)
s_alpha[i] = 1;
if (s_alpha[i] < 0)
s_alpha[i] = 0;
}
s_sigma = params[(2 * count_levels)];
if (kernel == "Matern")
s_n = static_cast<unsigned int>(params[(2 * count_levels) + 1] + 0.5);
s_aloc_init = false;
{ // for loaded parameters
if (s_kernel_id == 3)
{
s_params = new T[(2 * count_levels) + 2];
if(!s_params) laerror("allocation error in REGSURF::Init");
s_Matern_comb_number = new T[s_n];
if(!s_Matern_comb_number) laerror("allocation error in REGSURF::Init");
for (i = 0; i < ((2 * count_levels) + 2); i++)
s_params[i] = params[i];
}
else
{
s_params = new T[(2 * count_levels) + 1];
if(!s_params) laerror("allocation error in REGSURF::Init");
for (i = 0; i < ((2 * count_levels) + 1); i++)
s_params[i] = params[i];
}
}
// creating the list of numbers of geometries with minima of PES
for (i = 0; i < count_levels; i++)
{
min_value = values[i * count_geometries];
s_y_min[i] = 0;
for (j = 1; j < count_geometries; j++)
if (values[(i * count_kernels) + j] < min_value)
{
min_value = values[(i * count_kernels) + j];
s_y_min[i] = j;
}
}
// NRMat for input loaded geometries [s_count_dimensions * s_count_kernels, s_count_geometries]
s_geometries_mat = NRMat<T>(geometries, count_dimensions * count_kernels, count_geometries);
// NRMat for vector of y of geomeries for surface [s_count_geometries, s_count_levels]
s_y_mat = NRMat<T>(values, count_levels, count_geometries);
// NRVec for vector of Z for coulomb matrix descriptor [s_count_kernels]
if (Z != nullptr)
s_Z_vec = NRVec<unsigned int>(Z, count_kernels);
// NRMat for coordinate distances for geometries [s_count_kernels, s_count_kernels * s_count_geometries]
s_distances_mat = NRMat<T>(count_kernels, count_kernels * count_geometries);
// NRMat for matrix of descriptors between kernels in geometry [s_count_kernels, s_count_kernels * s_count_geometries]
s_desc_mat = NRMat<T>(count_kernels, count_kernels * count_geometries);
// NRVec of NRMat for matrix of kernel coefficients for kernels in geometries
// [s_count_kernels, s_count_kernels * s_count_geometries]
s_kernels_vec_mat = NRVec<NRMat<T>>(count_geometries);
for (i = 0; i < count_geometries; i++)
s_kernels_vec_mat[i] = NRMat<T>(count_kernels, count_kernels);
// NRVec for sums of kernels rows for base geometry [s_count_kernels]
s_kernels_rows_sums_vec_vec = NRVec<NRVec<T>>(count_geometries);
for (i = 0; i < count_geometries; i++)
s_kernels_rows_sums_vec_vec[i] = NRVec<T>(count_kernels);
// NRVec of NRVec for differences of sums kernels rows between base and other geometries [s_count_kernels, s_count_geometries]
s_kernels_diff_vec_vec = NRVec<NRVec<T>>(count_geometries);
for (i = 0; i < count_geometries; i++)
s_kernels_diff_vec_vec[i] = NRVec<T>(count_kernels);
// NRVec for differences of sums kernels rows and columns between base and other geometries [s_count_geometries]
s_kernels_diff_sums_vec = NRVec<T>(count_geometries);
// NRVec of NRVec for vector of weight coefficients of surface kernels in surface [s_count_kernels, s_count_levels]
s_weights_vec_vec = NRVec<NRVec<T>>(count_levels);
for (i = 0; i < count_levels; i++)
s_weights_vec_vec[i] = NRVec<T>(count_kernels);
// NRVec for vector of y of geomeries for surface [s_count_geometries]
s_y_vec = NRVec<T>(s_y_mat.operator T*(), count_geometries);
// NRVec for vector of y predictions of geomeries for surface [s_count_geometries]
s_y_preds_vec = NRVec<T>(count_geometries);
// NRVec for vector of delta y values for surface [s_count_geometries]
s_y_delta_vec = NRVec<T>(count_geometries);
// NRVec for vector of temporary y values for surface [s_count_geometries]
s_y_temp_vec = NRVec<T>(count_kernels);
// NRVec for vector of partial temporary y values for geometry [s_count_kernels]
s_y_temp_part_vec = NRVec<T>(count_kernels);
// NRMat for distances in geometry for predictions [s_count_kernels, s_count_kernels]
s_distances_pred_mat = NRMat<T>(s_count_kernels, s_count_kernels);
// NRMat for matrix of descriptors between kernels in geometry for predictions [s_count_kernels, s_count_kernels]
s_desc_pred_mat = NRMat<T>(s_count_kernels, s_count_kernels);
// NRVec of NRMat for matrix of kernel coefficients for kernels in geometry for predictions [count_kernels * count_kernels]
s_kernels_pred_vec_mat = NRVec<NRMat<T>>(1);
s_kernels_pred_vec_mat[0] = NRMat<T>(s_count_kernels, s_count_kernels);
// for loaded geometries and surfaces
s_geometries = s_geometries_mat.operator T*();
s_y = s_y_mat.operator T*();
// NRVec for vector of Z for coulomb matrix descriptor
s_Z = s_Z_vec.operator unsigned int*();
// for computing weights coefficiensts of kernels and making predictions
s_distances = s_distances_mat.operator T*();
s_desc = s_desc_mat.operator T*();
s_distances = s_distances_mat.operator T*();
s_kernels = s_kernels_vec_mat[0].operator T*();
s_kernels_rows_sums = s_kernels_rows_sums_vec_vec[0].operator T*();
s_kernels_diff = s_kernels_diff_vec_vec[0].operator T*();
s_kernels_diff_sums = s_kernels_diff_sums_vec.operator T*();
s_weights = s_weights_vec_vec[0].operator T*();
s_y_preds = s_y_preds_vec.operator T*();
s_y_delta = s_y_delta_vec.operator T*();
s_y_temp = s_y_temp_vec.operator T*();
s_y_temp_part = s_y_temp_part_vec.operator T*();
s_distances_pred = s_distances_pred_mat.operator T*();
s_desc_pred = s_desc_pred_mat.operator T*();
s_kernels_pred = s_kernels_pred_vec_mat[0].operator T*();
s_aloc_init = true;
// calculating Matern coefficients, if Matern kernel is selected
if (s_kernel_id == 3)
Matern_coefficient(s_n, s_Matern_comb_number);
s_base_init = true;
return;
}
template <typename T>
T REGSURF<T>::Kernel(const T* x, const unsigned int dimensions, const unsigned int kernel_id)
{
T result;
switch (kernel_id) {
case 0:
result = Gaussian_kernel(x, dimensions, s_sigma);
break;
case 1:
result = Laplacian_kernel(x, dimensions, s_sigma);
break;
case 2:
result = Laplacian_kernel(x, dimensions, s_sigma);
break;
case 3:
result = Matern_kernel(x, dimensions, s_sigma, s_n, s_Matern_comb_number);
break;
}
return(result);
}
template <typename T>
unsigned int REGSURF<T>::Build_s_distances_mat(const unsigned int count_geometries, const T* geometries, T* distances)
{
unsigned int i, j, k, l;
unsigned int count_dimensions, count_kernels;
T distance;
count_dimensions = s_count_dimensions;
count_kernels = s_count_kernels;
// filling the coordinate distances matrix
if (s_base_init == true)
for (i = 0; i < count_geometries; i++)
for (j = 0; j < count_kernels; j++)
for (k = 0; k < count_kernels; k++)
{
distance = 0;
for (l = 0; l < count_dimensions; l++)
{
distance += std::pow(geometries[(i * count_kernels * count_dimensions) + (j * count_dimensions) + l] -
geometries[(i * count_kernels * count_dimensions) + (k * count_dimensions) + l], 2);
}
distances[(i * count_kernels * count_kernels) + (j * count_kernels) + k] = std::pow(distance, 0.5);
}
else
{
laerror("regression model not initialized");
return(1);
}
return(0);
}
template <typename T>
unsigned int REGSURF<T>::Build_s_descriptor_mat(const unsigned int count_geometries, const T* distances, T* desc)
{
unsigned int i, j, k, l;
unsigned int count_kernels, count_dimensions;
T distance, distance_ref;
T descriptor;
count_dimensions = s_count_dimensions;
count_kernels = s_count_kernels;
// filling the descriptor matrix
if (s_base_init == true)
{
// the RE descriptor
switch (s_descriptor_id) {
case 0:
Build_s_RE_descriptor_mat(s_count_kernels, count_geometries, distances, desc, s_distances);
break;
case 1:
Build_s_coulomb_matrix_descriptor_mat(s_count_kernels, count_geometries, distances, desc, s_Z);
break;
case 2:
Build_s_inverse_pairs_descriptor_mat(s_count_kernels, count_geometries, distances, desc);
break;
case 3:
extern_desc(s_count_kernels, count_geometries, distances, desc);
break;
laerror("unknown decsriptor identification number");
return(1);
}
}
return(0);
}
template <typename T>
unsigned int REGSURF<T>::Build_s_kernels_mat(const unsigned int count_geometries, const T* desc, NRVec<NRMat<T>> &kernels_vec_mat)
{
unsigned int i, j, k;
unsigned int count_kernels;
T descriptor_difference;
T* matrix;
count_kernels = s_count_kernels;
// create matrices of kernel coefficients for each geometry
for (i = 0; i < count_geometries; i++)
{
matrix = kernels_vec_mat[i].operator T*();
// creating upper part of matrix of kernel coefficinnts and the diagonal
for (j = 0; j < count_kernels; j++)
for (k = j + 1; k < count_kernels; k++)
{
descriptor_difference = desc[(i * count_kernels * count_kernels) + (j * count_kernels) + k] -
s_desc[(j * count_kernels) + k];
matrix[(j * count_kernels) + k] = Kernel(&descriptor_difference, 1, s_kernel_id);
}
// creating the diagonal
for (j = 0; j < count_kernels; j++)
{
if (desc[(i * count_kernels * count_kernels) + j * (count_kernels + 1)] != 0)
{
descriptor_difference = desc[(i * count_kernels * count_kernels) + j * (count_kernels + 1)] -
s_desc[(i * count_kernels * count_kernels) + j * (count_kernels + 1)];
matrix[j * (count_kernels + 1)] = Kernel(&descriptor_difference, 1, s_kernel_id);;
}
else
matrix[j * (count_kernels + 1)] = 0;
}
// copying upper part of matrix down
for (j = 0; j < count_kernels; j++)
for (k = 0; k < j; k++)
matrix[(j * count_kernels) + k] = matrix[(k * count_kernels) + j];
}
return(0);
}
template <typename T>
unsigned int REGSURF<T>::Build_s_kernels_mat_sums()
{
unsigned int i, j, k;
unsigned int count_kernels, count_geometries;
T sum;
T* matrix;
T* vector;
T* temp;
T* kernels_mat;
count_geometries = s_count_geometries;
count_kernels = s_count_kernels;
temp = s_y_temp_vec.operator T*();
// create matrix of kernel sums differences for each kernel geometry
// fixed bug on rsum() function
for (i = 0; i < count_geometries; i++)
{
matrix = s_kernels_vec_mat[i].operator T*();
vector = s_kernels_rows_sums_vec_vec[i].operator T*();
for (j = 0; j < count_kernels; j++)
{
sum = 0;
for (k = 0; k < count_kernels; k++)
sum += matrix[(j * count_kernels) + k];
vector[j] = sum;
}
}
for (i = 0; i < count_geometries; i++)
{
kernels_mat = s_kernels_vec_mat[i].operator T*();
for (j = 0; j < count_kernels; j++)
{
sum = 0;
for (k = 0; k < count_kernels; k++)
sum += kernels_mat[(j * count_kernels) + k];
temp[j] = sum;
}
s_kernels_diff_vec_vec[i] |= s_kernels_rows_sums_vec_vec[0];
s_kernels_diff_vec_vec[i] -= s_y_temp_vec;
}
// create vector of kernel sums differences for each geometry
for (i = 0; i < count_geometries; i++)
s_kernels_diff_sums_vec[i] = s_kernels_diff_vec_vec[i].sum();
// calculation of kernels matrix sum for base geometry
s_kernels_sum_sum = T(count_kernels * count_kernels);
return(0);
}
template <typename T>
T REGSURF<T>::s_RMSE(const unsigned int level)
{
unsigned int i;
T delta_square;
T Lasso_part;
T kernel_ridge_part;
T sum_kernels_base;
T y_pred_base;
T y_pred;
// check the initialization
if (s_base_init == false)
{
laerror("regression model not initialized");
return(1);
}
if (level >= s_count_levels)
{
laerror("level number out of model range");
return(1);
}
if (s_descriptor_id == 1)
sum_kernels_base = s_count_kernels * s_count_kernels;
else
sum_kernels_base = s_count_kernels * (s_count_kernels - 1.00);
Lasso_part = s_lambda[level] * s_alpha[level];
kernel_ridge_part = s_lambda[level] * (1 - s_alpha[level]) * 0.5 * s_count_kernels;
// predictions for all geometries
for (i = 0; i < s_count_geometries; i++)
{
s_y_temp_part_vec |= s_kernels_rows_sums_vec_vec[i];
s_y_temp_part_vec *= s_weights_vec_vec[level];
y_pred = s_y_temp_part_vec.sum();
if (kernel_ridge_part != 0)
y_pred *= (sum_kernels_base - s_kernels_diff_sums_vec[i] + kernel_ridge_part)/
(sum_kernels_base - s_kernels_diff_sums_vec[i]);
y_pred += Lasso_part;
s_y_preds[i] = y_pred;
}
s_y_delta_vec |= s_y_preds_vec;
// subtracting real values
s_y_delta_vec -= s_y_mat.row(level);
// the second power
s_y_delta_vec *= s_y_delta_vec;
// the sum
delta_square = s_y_delta_vec.sum();
// calculating RMSE
delta_square = delta_square/T(s_count_geometries);
delta_square = std::sqrt(delta_square);
return(delta_square);
}
template <typename T>
T REGSURF<T>::Loss_Function(const unsigned int level)
{
unsigned int i;
unsigned int count_x;
T RMSE;
T KRR_part;
T Lasso_part;
// check the initialization
if (s_base_init == false)
{
laerror("regression model not initialized");
return(1);
}
// calculating the root mean square error - RMSE
RMSE = s_RMSE(level);
return (RMSE);
}
template <typename T>
unsigned int REGSURF<T>::s_Fit(const T max_Loss, const unsigned int max_learning_cycles, const T learning_rate,
const bool element_invariant, unsigned int* kernel_classes)
{
unsigned int i, j, k, l, m;
unsigned int count_kernels, count_geometries, count_levels;
unsigned int element_count;
unsigned int* kernel_classes_index;
T init_weight;
T y_pred, y_pred_2, y_pred_3;
T y_diff;
T Loss;
T Lasso_part;
T kernel_ridge_part;
T element_sum;
T sum_kernels_base;
T y_pred_base;
count_kernels = s_count_kernels;
count_geometries = s_count_geometries;
count_levels = s_count_levels;
if (kernel_classes == nullptr)
kernel_classes_index = s_Z;
else
kernel_classes_index = kernel_classes;
// check the initialization
if (s_base_init == false)
{
laerror("regression model not initialized");
return(1);
}
// checking the input parameters
if (max_learning_cycles == 0)
{
laerror("zero learning cycles");
return(1);
}
if (max_Loss < 0)
{
laerror("negative value of maximum Loss function");
return(1);
}
// initialization of s_distances_mat and s_kernels_mat if necessary
if (s_distances_mat_init == false)
{
Build_s_distances_mat(s_count_geometries, s_geometries, s_distances);
s_distances_mat_init = true;
}
if (s_descriptors_mat_init == false)
{
Build_s_descriptor_mat(s_count_geometries, s_distances, s_desc);
s_descriptors_mat_init = true;
}
if (s_kernels_mat_init == false)
{
Build_s_kernels_mat(s_count_geometries, s_desc, s_kernels_vec_mat);
Build_s_kernels_mat_sums();
s_kernels_mat_init = true;
}
sum_kernels_base = s_kernels_rows_sums_vec_vec[0].sum();
// the training loop for all surfaces
for (i = 0; i < count_levels; i++)
{
Lasso_part = s_lambda[i] * s_alpha[i];
kernel_ridge_part = s_lambda[i] * (1 - s_alpha[i]) * 0.5 * count_kernels;
// initialitation of weight coefficients vector
init_weight = (s_y[i] - Lasso_part)/(s_kernels_sum_sum + kernel_ridge_part);
for (j = 0; j < count_kernels; j++)
s_weights_vec_vec[i][j] = init_weight;
for (j = 0; j < max_learning_cycles; j++)
{
for (k = 1; k < count_geometries; k++)
{
// get the model prediction for another geometry
s_y_temp_part_vec |= s_kernels_rows_sums_vec_vec[k];
s_y_temp_part_vec *= s_weights_vec_vec[i];
y_pred = s_y_temp_part_vec.sum();
// calculating kernel ridge part of prediction
if (kernel_ridge_part != 0)
y_pred_2 = y_pred * kernel_ridge_part/(sum_kernels_base - s_kernels_diff_sums_vec[k]);
else
y_pred_2 = 0;
y_pred_3 = y_pred + y_pred_2 + Lasso_part;
// calculating difference
y_diff = y_pred_3 - s_y[k + (i * count_geometries)];
// calculating new weights
s_y_temp_part_vec |= s_kernels_diff_vec_vec[k];
s_y_temp_part_vec /= s_kernels_diff_sums_vec[k];
// optimalization of kernel ridge regression
if (kernel_ridge_part != 0)
{
s_y_temp_vec |= s_weights_vec_vec[i];
s_y_temp_vec /= s_weights_vec_vec[i].sum()/y_pred_2;
s_y_temp_part_vec -= s_y_temp_vec;
}
if (element_invariant == true and kernel_classes_index != nullptr)
{
// for element ivariant kernels
for (l = 0; l < count_kernels; l++)
{
element_sum = 0;
element_count = 0;
for (m = 0; m < count_kernels; m++)
{
if (kernel_classes_index[m] == kernel_classes_index[l] and m < l)
break;
if (kernel_classes_index[m] == kernel_classes_index[l] and m >= l)
{
element_sum += s_y_temp_part[l];
element_count += 1;
}
}
if (element_count > 1)
{
element_sum /= element_count;
s_y_temp_part[l] = element_sum;
for (m = l + 1; m < count_kernels; m++)
{
if (kernel_classes_index[m] == kernel_classes_index[l])
s_y_temp_part[m] = element_sum;
}
}
}
}
s_y_temp_part_vec /= (T(count_geometries)/(learning_rate * y_diff));
s_weights_vec_vec[i] -= s_y_temp_part_vec;
}
// convergence criteria check
if (max_Loss > 0)
{
Loss = Loss_Function(i);
if (Loss < max_Loss)
break;
}
}
}
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Save_fitted(const string weights)
{
unsigned int i, j;
unsigned int count_kernels, count_levels;
ofstream weightsfile(weights);
string line;
count_kernels = s_count_kernels;
count_levels = s_count_levels;
if (weightsfile.is_open())
{
for (i = 0; i < count_levels; i++)
{
for (j = 0; j < count_kernels; j++)
{
line = to_string(s_weights_vec_vec[i][j]);
weightsfile << line << '\n';
}
}
weightsfile.close();
}
else
{
laerror("not open file for weights");
return(1);
}
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Load_fitted(const string weights)
{
unsigned int i, j;
unsigned int count_kernels, count_levels;
ifstream weightsfile(weights);
string line;
T line_value;
count_kernels = s_count_kernels;
count_levels = s_count_levels;
// initialization of REG_distances_mat and REG_kernels_mat if necessary
if (s_distances_mat_init == false)
{
Build_s_distances_mat(s_count_geometries, s_geometries, s_distances);
s_distances_mat_init = true;
}
if (s_descriptors_mat_init == false)
{
Build_s_descriptor_mat(s_count_geometries, s_distances, s_desc);
s_descriptors_mat_init = true;
}
if (s_kernels_mat_init == false)
{
Build_s_kernels_mat(s_count_geometries, s_desc, s_kernels_vec_mat);
Build_s_kernels_mat_sums();
s_kernels_mat_init = true;
}
if (weightsfile.is_open())
{
for (i = 0; i < count_levels; i++)
for (j = 0; j < count_kernels; j++)
{
if (getline(weightsfile, line))
{
line_value = stod(line);
s_weights_vec_vec[i][j] = line_value;
}
else
{
laerror("not open file with weights");
return(1);
}
}
weightsfile.close();
}
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Get_predictions(const unsigned int count_geometries, const unsigned int count_levels,
const T* geom, const unsigned int* surfaces, T* predictions)
{
unsigned int i, j, k;
unsigned int count_kernels;
T distance;
T Lasso_part;
T kernel_ridge_part;
T sum_kernels;
T pred_1, pred_2, pred_3;
if (s_base_init == false)
{
laerror("regression model not inicialized");
return(1);
}
for (i = 0; i < count_levels; i++)
if (surfaces[i] >= s_count_levels)
{
laerror("invalid number of surface level");
return(1);
}
count_kernels = s_count_kernels;
for (i = 0; i < count_geometries; i++)
{
// building distances, descriptors and kernels matrices
Build_s_distances_mat(1, geom + (i * s_count_dimensions * s_count_kernels), s_distances_pred);
Build_s_descriptor_mat(1, s_distances_pred, s_desc_pred);
Build_s_kernels_mat(1, s_desc_pred, s_kernels_pred_vec_mat);
// replacing errored rsum() function
for (j = 0; j < count_kernels; j++)
{
sum_kernels = 0;
for (k = 0; k < count_kernels; k++)
sum_kernels += s_kernels_pred[(j * count_kernels) + k];
s_y_temp[j] = sum_kernels;
}
sum_kernels = s_y_temp_vec.sum();
for (j = 0; j < count_levels; j++)
{
Lasso_part = s_lambda[surfaces[j]] * s_alpha[surfaces[j]];
kernel_ridge_part = s_lambda[surfaces[j]] * (1 - s_alpha[surfaces[j]]) * 0.5 * count_kernels;
// getting prediction
if (surfaces[j] < s_count_levels)
{
s_y_temp_part_vec |= s_y_temp_vec;
s_y_temp_part_vec *= s_weights_vec_vec[surfaces[j]];
}
else
{
laerror("invalid number of surface level");
return(1);
}
pred_1 = s_y_temp_part_vec.sum();
if (kernel_ridge_part != 0)
{
pred_2 = pred_1 * kernel_ridge_part/sum_kernels;
}
else
pred_2 = 0;
pred_3 = Lasso_part;
predictions[(i * count_levels) + j] = pred_1 + pred_2 + pred_3;
}
}
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Score(const unsigned int count_geometries, const unsigned int count_levels,
const T* geom, const unsigned int* surfaces, T* predictions, T* y_values, T* score)
{
unsigned int i;
T RMSE;
s_Get_predictions(count_geometries, count_levels, geom, surfaces, predictions);
for (i = 0; i < count_geometries * count_levels; i++)
RMSE += (predictions[i] - y_values[i]) * (predictions[i] - y_values[i]);
RMSE = RMSE/(count_geometries * count_levels);
RMSE = std::sqrt(RMSE);
score[0] = RMSE;
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Set_params(const string descriptor, const string kernel, const T* params)
{
unsigned int i;
unsigned int count_levels;
unsigned int new_descriptor_id;
unsigned int new_kernel_id;
if (s_base_init == false)
{
laerror("regression model not initialized");
return(1);
}
count_levels = s_count_levels;
s_kernel_type = kernel;
if (kernel != "Gaussian" and kernel != "Laplacian" and kernel != "Exponential" and kernel != "Matern")
{
laerror("invalid type of kernel");
return(1);
}
if (kernel == "Gaussian")
new_kernel_id = 0;
if (kernel == "Laplacian")
new_kernel_id = 1;
if (kernel == "Exponential")
new_kernel_id = 2;
if (kernel == "Matern")
new_kernel_id = 3;
s_descriptor_type = descriptor;
if (descriptor == "RE")
new_descriptor_id = 0;
if (descriptor == "Coulomb_matrix")
new_descriptor_id = 1;
if (descriptor == "Inverse_pairs")
new_descriptor_id = 2;
if (new_kernel_id != s_kernel_id)
{
s_kernel_id = new_kernel_id;
s_kernels_mat_init = false;
}
if (new_descriptor_id != s_descriptor_id)
{
s_descriptor_id = new_descriptor_id;
s_kernels_mat_init = false;
s_descriptors_mat_init = false;
}
// copy parameters and data
for (i = 0; i < count_levels; i++)
s_lambda[i] = params[i];
for (i = count_levels; i < (2 * count_levels); i++)
{
s_alpha[i - count_levels] = params[i];
if (s_alpha[i - count_levels] > 1)
s_alpha[i - count_levels] = 1;
if (s_alpha[i - count_levels] < 0)
s_alpha[i - count_levels] = 0;
}
s_sigma = params[2 * count_levels];
if (s_kernel_id == 3)
for (i = 0; i < ((2 * count_levels) + 2); i++)
s_params[i] = params[i];
else
for (i = 0; i < ((2 * count_levels) + 1); i++)
s_params[i] = params[i];
if (s_kernel_id == 3)
{
s_n = static_cast<unsigned int>(params[(2 * count_levels) + 1] + 0.5);
if (s_Matern_comb_number != nullptr)
{
delete[] s_Matern_comb_number;
s_Matern_comb_number = nullptr;
}
{
s_Matern_comb_number = new T[s_n];
if(!s_Matern_comb_number) laerror("allocation error of s_Matern_comb_number");
Matern_coefficient(s_n, s_Matern_comb_number);
}
}
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Get_params(string* descriptor, string* kernel, T* params)
{
unsigned int i;
unsigned int count_levels;
if (s_base_init == false)
{
laerror("regression model not initialized");
return(1);
}
count_levels = s_count_levels;
descriptor[0] = s_descriptor_type;
kernel[0] = s_kernel_type;
if (s_kernel_id != 3)
for (i = 0; i < ((2 * count_levels) + 1); i++)
params[i] = s_params[i];
else
for (i = 0; i < ((2 * count_levels) + 2); i++)
params[i] = s_params[i];
return(0);
}
template <typename T>
unsigned int REGSURF<T>:: s_Set_extern_descriptor(unsigned int (*extern_func)(const unsigned int, const unsigned int, const T*, T*))
{
extern_desc = extern_func;
extern_descriptor = true;
s_descriptor_id = 3;
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Get_weight_coeff(const unsigned int s_number, T* weights)
{
unsigned int i;
unsigned int count_kernels;
count_kernels = s_count_kernels;
for (i = 0; i < count_kernels; i++)
weights[i] = s_weights_vec_vec[s_number][i];
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Load_ML_NEA_geometries(const unsigned int count_geometries, const T* geometries)
{
ML_NEA_count_geometries = count_geometries;
s_ML_NEA_geometries_mat = NRMat<T>(geometries, s_count_dimensions * s_count_kernels, count_geometries);
s_ML_NEA_geometries = s_ML_NEA_geometries_mat.operator T*();
s_ML_NEA_input_init = true;
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Load_ML_NEA_geometries(const unsigned int count_geometries, const string geometries_file)
{
unsigned int i, j, k, l;
unsigned int count_dimensions, count_kernels;
unsigned int line_size, numbers_size;
ifstream geomfile(geometries_file);
string line;
T line_value;
const string numbers = "+-0123456789.,";
string readed_number;
bool read_switch;
bool decimal_switch;
count_dimensions = s_count_dimensions;
count_kernels = s_count_kernels;
ML_NEA_count_geometries = count_geometries;
s_ML_NEA_geometries_mat = NRMat<T>(s_count_dimensions * s_count_kernels, count_geometries);
s_ML_NEA_geometries = s_ML_NEA_geometries_mat.operator T*();
l = 0;
numbers_size = numbers.size();
if (geomfile.is_open())
{
for (i = 0; i < 3 * count_dimensions * count_kernels * count_geometries; i++)
{
if (getline(geomfile, line))
{
read_switch = false;
decimal_switch = false;
readed_number = "";
line_size = line.size();
for (j = 0; j < line_size; j++)
{
read_switch = false;
for (k = 0; k < numbers_size; k++)
{
if (line[j] == numbers[k])
{
readed_number.append(line, j, 1);
read_switch = true;
if (line[j] == numbers[12] or line[j] == numbers[13])
decimal_switch = true;
}
}
if (read_switch == false)
{
if (decimal_switch == true and readed_number != "")
{
line_value = stod(readed_number);
s_ML_NEA_geometries[l] = line_value;
l++;
}
readed_number = "";
read_switch = false;
decimal_switch = false;
}
}
if (decimal_switch == true and readed_number != "")
{
line_value = stod(readed_number);
s_ML_NEA_geometries[l] = line_value;
l++;
}
}
if (l == count_dimensions * count_kernels * count_geometries)
break;
}
geomfile.close();
}
else
{
laerror("file with geometry is not open");
return(1);
}
if (l < count_dimensions * count_kernels * count_geometries)
{
laerror("input ML-NEA geometries not complete");
return(1);
}
s_ML_NEA_input_init = true;
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Predict_ML_NEA_geometries()
{
unsigned int i;
T control_value;
// getting predistions
if (s_base_init == false)
{
laerror("regression model not initialized");
return(1);
}
if (s_ML_NEA_input_init == false)
{
laerror("ML-NEA model input data not initialized");
return(1);
}
s_ML_NEA_surf_numbers_vec = NRVec<unsigned int>(s_count_levels);
s_ML_NEA_surf_numbers = s_ML_NEA_surf_numbers_vec.operator unsigned int*();
for (i = 0; i < s_count_levels; i++)
s_ML_NEA_surf_numbers[i] = i;
s_ML_NEA_y_mat = NRMat<T>(ML_NEA_count_geometries, s_count_levels);
s_ML_NEA_y = s_ML_NEA_y_mat.operator T*();
control_value = s_Get_predictions(ML_NEA_count_geometries, s_count_levels, s_ML_NEA_geometries, s_ML_NEA_surf_numbers, s_ML_NEA_y);
if (control_value == 0)
s_ML_NEA_surf_init = true;
return(control_value);
}
template <typename T>
unsigned int REGSURF<T>::s_Compute_ML_NEA_spectra(const T E_min, const T step, const unsigned int count_steps, const T delta)
{ // sigma(E) = (e^2 *h)/(4*m_e*c*E_0*E) * sum_{n=1}^N\(1/Np * sum_{i=1}^Np * delta_E_0n(x_i) * f_0n(x_i) * f_g(E - delta_E_0n, delta))
// delta_E_0n(x_i) = delta_E_min_n/delta_E_0n
// f_g = 1/(sqrt(Phi) * delta * delta) * exp(-((E-delta_E_0n)^2)/(0.5 * delta * delta))
unsigned int i, j, k;
unsigned int count_kernels, count_levels, count_geometries;
T y_min;
T h = 6.62607015E-34;
T e = 1.602176634E-19;
T m_e = 9.1093837E-31;
T c = 299796000;
T E_0 = 8.8541878128E-12;
T Phi = (1.00 + std::sqrt(5))/2.00;
T pre_sum_part;
T E;
T delta_E_0n;
T f_0n;
T f_g;
T sigma_part;
count_kernels = s_count_kernels;
count_levels = s_count_levels;
count_geometries = ML_NEA_count_geometries;
if (s_base_init == false)
{
laerror("regression model not initialized");
return(1);
}
if (s_ML_NEA_input_init == false)
{
laerror("ML-NEA model input data not initialized");
return(1);
}
// calculating minima of surfaces
s_ML_NEA_min_y_vec = NRVec<T>(s_count_levels);
s_ML_NEA_min_y = s_ML_NEA_min_y_vec.operator T*();
for (i = 0; i < count_levels; i++)
{
y_min = s_ML_NEA_y[i * count_kernels];
for (j = 1; j < count_kernels; j++)
{
if (s_ML_NEA_y[(i * count_kernels) + j] < y_min)
y_min = s_ML_NEA_y[(i * count_kernels) + j];
}
s_ML_NEA_min_y[i] = y_min;
}
// calculating spectra
s_ML_NEA_spectra_vec = NRVec<T>(count_steps);
s_ML_NEA_spectra = s_ML_NEA_spectra_vec.operator T*();
pre_sum_part = (e * e * h)/(4 * m_e * c * E_0);
E = E_min;
for (i = 0; i < count_steps; i++)
s_ML_NEA_spectra[i] = 0;
for (i = 0; i < count_geometries; i++)
{
for (j = 0; j < count_levels; j++)
{
// calculating the oscilator strenght
delta_E_0n = s_ML_NEA_y[(j * count_geometries) + i];
f_0n = s_ML_NEA_min_y[j]/delta_E_0n;
E = E_min;
if (delta_E_0n > 0 and f_0n > 0)
for (k = 0; k < count_steps; k++)
{
// calculating gaussian function f_g partial intensity and adding to ML_NEA intensities vector
if (abs(E - delta_E_0n)/delta < 100)
{
f_g = 1/(std::sqrt(Phi) * delta);
f_g *= std::exp(-((E - delta_E_0n)*(E - delta_E_0n))/(0.5 * delta * delta));
if (E > 0 and f_g > 0)
{
sigma_part = pre_sum_part/E * delta_E_0n * f_0n * f_g;
s_ML_NEA_spectra[k] += sigma_part;
}
}
E += step;
}
}
}
s_ML_NEA_spectra_init = true;
return(0);
}
template <typename T>
unsigned int REGSURF<T>::s_Save_ML_NEA_spectra(const string ML_NEA_spectra_file)
{
unsigned int i;
unsigned int spectra_size;
T spectra_sum;
ofstream ML_NEA_file(ML_NEA_spectra_file);
string line;
spectra_size = s_ML_NEA_spectra_vec.size();
spectra_sum = s_ML_NEA_spectra_vec.sum();
if (ML_NEA_file.is_open())
{
for (i = 0; i < spectra_size; i++)
{
line = to_string(s_ML_NEA_spectra[i]/spectra_sum);
ML_NEA_file << line << '\n';
}
ML_NEA_file.close();
}
else
{
laerror("file for ML-NEA spectra not open");
return(1);
}
return(0);
}
template <typename T>
REGSURF<T>::~REGSURF()
{
// dealocating memory to avoid memory leaks
if (s_lambda != nullptr)
delete[] s_lambda;
if (s_alpha != nullptr)
delete[] s_alpha;
if (s_params != nullptr)
delete[] s_params;
if (s_Matern_comb_number != nullptr)
delete[] s_Matern_comb_number;
if (s_y_min != nullptr)
delete[] s_y_min;
if (s_geom_file != nullptr)
delete[] s_geom_file;
if (s_y_file != nullptr)
delete[] s_y_file;
if (s_Z_file != nullptr)
delete[] s_Z_file;
}
} // end of namespace
using namespace LA;
//enforce instantiation
template class REGSURF<double>;