From bc7fa54760fbee413805904eacce7f3027469e19 Mon Sep 17 00:00:00 2001
From: Carl Philipp Klemm <philipp@uvos.xyz>
Date: Fri, 28 Apr 2023 12:37:46 +0200
Subject: [PATCH] working minimization

---
 eigentorchconversions.h | 54 ++++++++++++++++++++++++--
 main.cpp                | 85 +++++++++++++++++++++--------------------
 2 files changed, 94 insertions(+), 45 deletions(-)

diff --git a/eigentorchconversions.h b/eigentorchconversions.h
index 24ab241..cedbf38 100644
--- a/eigentorchconversions.h
+++ b/eigentorchconversions.h
@@ -1,7 +1,44 @@
+#include <climits>
+#include <sys/types.h>
 #include <torch/torch.h>
 #include <Eigen/Dense>
+#include <torch/types.h>
 #include <vector>
 
+template <typename V>
+inline torch::TensorOptions tensorOptCpuNg()
+{
+	static_assert(std::is_same<V, float>::value || std::is_same<V, double>::value,
+				  "This function can only be passed double or float types");
+	torch::TensorOptions options;
+	if constexpr(std::is_same<V, float>::value)
+		options = options.dtype(torch::kFloat32);
+	else
+		options = options.dtype(torch::kFloat64);
+	options = options.layout(torch::kStrided);
+	options = options.device(torch::kCPU);
+	options = options.requires_grad(false);
+	return options;
+}
+
+template <typename V>
+bool checkTorchType(const torch::Tensor& tensor)
+{
+	static_assert(std::is_same<V, float>::value || std::is_same<V, double>::value ||
+		std::is_same<V, int64_t>::value || std::is_same<V, int32_t>::value || std::is_same<V, int8_t>::value,
+				  "This function dose not work with this type");
+	if constexpr(std::is_same<V, float>::value)
+		return tensor.dtype() == torch::kFloat32;
+	else if constexpr(std::is_same<V, double>::value)
+		return tensor.dtype() == torch::kFloat64;
+	else if constexpr(std::is_same<V, int64_t>::value)
+		return tensor.dtype() == torch::kInt64;
+	else if constexpr(std::is_same<V, int32_t>::value)
+		return tensor.dtype() == torch::kInt32;
+	else if constexpr(std::is_same<V, int8_t>::value)
+		return tensor.dtype() == torch::kInt8;
+}
+
 template <typename V>
 using MatrixXrm = typename Eigen::Matrix<V, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
 
@@ -10,7 +47,7 @@ torch::Tensor eigen2libtorch(Eigen::MatrixX<V> &M)
 {
 	Eigen::Matrix<V, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> E(M);
 	std::vector<int64_t> dims = {E.rows(), E.cols()};
-	auto T = torch::from_blob(E.data(), dims).clone(); //.to(torch::kCPU);
+	auto T = torch::from_blob(E.data(), dims, tensorOptCpuNg<V>()).clone();
 	return T;
 }
 
@@ -18,7 +55,7 @@ template <typename V>
 torch::Tensor eigen2libtorch(MatrixXrm<V> &E, bool copydata = true)
 {
 	std::vector<int64_t> dims = {E.rows(), E.cols()};
-	auto T = torch::from_blob(E.data(), dims);
+	auto T = torch::from_blob(E.data(), dims, tensorOptCpuNg<V>());
 	if (copydata)
 		return T.clone();
 	else
@@ -26,13 +63,24 @@ torch::Tensor eigen2libtorch(MatrixXrm<V> &E, bool copydata = true)
 }
 
 template<typename V>
-Eigen::Matrix<V, Eigen::Dynamic, Eigen::Dynamic> libtorch2eigen(torch::Tensor &Tin)
+Eigen::Matrix<V, Eigen::Dynamic, Eigen::Dynamic> libtorch2eigenMaxtrix(torch::Tensor &Tin)
 {
 	/*
 	LibTorch is Row-major order and Eigen is Column-major order.
 	MatrixXrm uses Eigen::RowMajor for compatibility.
 	*/
+	assert(checkTorchType<V>(Tin));
 	auto T = Tin.to(torch::kCPU);
 	Eigen::Map<MatrixXrm<V>> E(T.data_ptr<V>(), T.size(0), T.size(1));
 	return E;
 }
+
+template<typename V>
+Eigen::Vector<V, Eigen::Dynamic> libtorch2eigenVector(torch::Tensor &Tin)
+{
+	assert(Tin.sizes().size() == 1);
+	assert(checkTorchType<V>(Tin));
+	auto T = Tin.to(torch::kCPU);
+	Eigen::Map<Eigen::Vector<V, Eigen::Dynamic>> E(T.data_ptr<V>(), T.numel());
+	return E;
+}
diff --git a/main.cpp b/main.cpp
index 43e91f1..0fa6dee 100644
--- a/main.cpp
+++ b/main.cpp
@@ -32,24 +32,11 @@ void printImpedance(const std::vector<eis::DataPoint>& data)
 	std::cout<<"]\n";
 }
 
-torch::TensorOptions getTensorOptions()
-{
-	torch::TensorOptions options;
-	if constexpr(sizeof(fvalue) == sizeof(float))
-		options = options.dtype(torch::kFloat32);
-	else if constexpr(sizeof(fvalue) == sizeof(double))
-		options = options.dtype(torch::kFloat64);
-	options = options.layout(torch::kStrided);
-	options = options.device(torch::kCPU);
-	options = options.requires_grad(false);
-	return options;
-}
-
 torch::Tensor eisToTensor(const std::vector<eis::DataPoint>& data, torch::Tensor* freqs)
 {
-	torch::Tensor output = torch::empty({static_cast<long int>(data.size()*2)}, getTensorOptions());
+	torch::Tensor output = torch::empty({static_cast<long int>(data.size()*2)}, tensorOptCpuNg<fvalue>());
 	if(freqs)
-		*freqs = torch::empty({static_cast<long int>(data.size()*2)}, getTensorOptions());
+		*freqs = torch::empty({static_cast<long int>(data.size()*2)}, tensorOptCpuNg<fvalue>());
 
 	float* tensorDataPtr = output.contiguous().data_ptr<float>();
 	float* tensorFreqDataPtr = freqs ? freqs->contiguous().data_ptr<float>() : nullptr;
@@ -70,12 +57,12 @@ torch::Tensor eisToTensor(const std::vector<eis::DataPoint>& data, torch::Tensor
 
 torch::Tensor fvalueVectorToTensor(std::vector<fvalue>& vect)
 {
-	return torch::from_blob(vect.data(), {static_cast<int64_t>(vect.size())}, getTensorOptions());
+	return torch::from_blob(vect.data(), {static_cast<int64_t>(vect.size())}, tensorOptCpuNg<fvalue>());
 }
 
 torch::Tensor guesStartingPoint(torch::Tensor& omega, torch::Tensor& impedanceSpectra)
 {
-	torch::Tensor startingPoint = torch::zeros(omega.sizes(), getTensorOptions());
+	torch::Tensor startingPoint = torch::zeros(omega.sizes(), tensorOptCpuNg<fvalue>());
 	startingPoint[-1] = torch::abs(impedanceSpectra[-1]);
 	return startingPoint;
 }
@@ -126,26 +113,33 @@ torch::Tensor aReal(torch::Tensor& omega)
 	return out;
 }
 
+/*def S(gamma_R_inf, Z_exp_re, Z_exp_im, A_re, A_im, el):
+    MSE_re = np.sum((gamma_R_inf[-1] + np.matmul(A_re, gamma_R_inf[:-1]) - Z_exp_re)**2)
+    MSE_im = np.sum((np.matmul(A_im, gamma_R_inf[:-1]) - Z_exp_im)**2)
+    reg_term = el/2*np.sum(gamma_R_inf[:-1]**2)
+    obj = MSE_re + MSE_im + reg_term
+    return obj
+
 torch::Tensor tikhnovDrt(torch::Tensor& omega, torch::Tensor& impedanceSpectra, fvalue regularaziaion = 1e-2)
 {
 	torch::Tensor aMatrixImag = aImag(omega);
 	torch::Tensor aMatrixReal = aReal(omega);
 	torch::Tensor startingPoint = guesStartingPoint(omega, impedanceSpectra);
 
-	torch::Tensor bounds = torch::zeros({startingPoint.size(0), 1}, getTensorOptions());
-	bounds = torch::cat({bounds, torch::zeros({startingPoint.size(0), 1}, getTensorOptions())*torch::max(torch::abs(impedanceSpectra))});
+	torch::Tensor bounds = torch::zeros({startingPoint.size(0), 1}, tensorOptCpuNg<fvalue>());
+	bounds = torch::cat({bounds, torch::zeros({startingPoint.size(0), 1}, tensorOptCpuNg<fvalue>())*torch::max(torch::abs(impedanceSpectra))});
 
 	std::cout<<"startingPoint:\n "<<startingPoint<<'\n';
 	std::cout<<"bounds:\n "<<bounds<<'\n';
 
-	/*result = minimize(S, x0, args=(Z_exp_re, Z_exp_im, A_re, A_im, el), method=method,
+	result = minimize(S, x0, args=(Z_exp_re, Z_exp_im, A_re, A_im, el), method=method,
 					bounds = bounds, options={'disp': True, 'ftol':1e-10, 'maxiter':200})
 	gamma_R_inf = result.x
 	R_inf = gamma_R_inf[-1]
 	gamma = gamma_R_inf[:-1]
-	return gamma, R_inf*/
+	return gamma, R_inf
 	return bounds;
-}
+}*/
 
 class RtFunct
 {
@@ -153,11 +147,11 @@ private:
 	torch::Tensor impedanceSpectra;
 	torch::Tensor aMatrixImag;
 	torch::Tensor aMatrixReal;
-	double el;
-	double epsilon;
+	fvalue el;
+	fvalue epsilon;
 
 public:
-	RtFunct(torch::Tensor impedanceSpectraI, torch::Tensor aMatrixImagI, torch::Tensor aMatrixRealI, double elI, double epsilonI):
+	RtFunct(torch::Tensor impedanceSpectraI, torch::Tensor aMatrixImagI, torch::Tensor aMatrixRealI, fvalue elI, fvalue epsilonI):
 	impedanceSpectra(impedanceSpectraI),
 	aMatrixImag(aMatrixImagI),
 	aMatrixReal(aMatrixRealI),
@@ -167,39 +161,42 @@ public:
 
 	}
 
-	static double function(const torch::Tensor& x)
+	static fvalue function(const torch::Tensor& x)
 	{
-		auto xAccessor = x.accessor<double, 1>();
-		double accum = 0;
+		assert(checkTorchType<fvalue>(x));
+		auto xAccessor = x.accessor<fvalue, 1>();
+		fvalue accum = 0;
 		for(int64_t i = 0; i < x.size(0); ++i)
-			accum += xAccessor[i]*xAccessor[i];
+			accum += (xAccessor[i]+3)*(xAccessor[i]+3)+20;
 		return accum;
 	}
 
-	static torch::Tensor getGrad(std::function<double(const torch::Tensor& x)> fn, const torch::Tensor& xTensor, double epsilon)
+	static torch::Tensor getGrad(std::function<fvalue(const torch::Tensor& x)> fn, const torch::Tensor& xTensor, fvalue epsilon)
 	{
-		torch::Tensor out = torch::zeros(xTensor.sizes(), getTensorOptions());
+		torch::Tensor out = torch::zeros(xTensor.sizes(), tensorOptCpuNg<fvalue>());
 		auto outAccessor = out.accessor<fvalue, 1>();
-		auto xAccessor = xTensor.accessor<double, 1>();
+		assert(checkTorchType<fvalue>(xTensor));
+		auto xAccessor = xTensor.accessor<fvalue, 1>();
 		for(int64_t i = 0; i < out.size(0); ++i)
 		{
 			xAccessor[i] -= epsilon;
-			double left = fn(xTensor);
+			fvalue left = fn(xTensor);
 			xAccessor[i] += 2*epsilon;
-			double right = fn(xTensor);
+			fvalue right = fn(xTensor);
 			xAccessor[i] -= epsilon;
 			outAccessor[i] = (right-left)/(2*epsilon);
 		}
 		return out;
 	}
 
-	double operator()(const Eigen::VectorXd& x, Eigen::VectorXd& grad)
+	fvalue operator()(const Eigen::VectorX<fvalue>& x, Eigen::VectorX<fvalue>& grad)
 	{
-		Eigen::MatrixX<double> xMatrix = x;
-		torch::Tensor xTensor = eigen2libtorch(xMatrix).reshape({xTensor.numel()});
+		Eigen::MatrixX<fvalue> xMatrix = x;
+		torch::Tensor xTensor = eigen2libtorch(xMatrix);
+		xTensor = xTensor.reshape({xTensor.numel()});
 		std::cout<<"xTensor\n "<<xTensor<<'\n';
 		torch::Tensor gradTensor = getGrad(&function, xTensor, epsilon);
-		grad = libtorch2eigen<double>(gradTensor);
+		grad = libtorch2eigenVector<fvalue>(gradTensor);
 		return function(xTensor);
 	}
 };
@@ -224,15 +221,19 @@ int main(int argc, char** argv)
 
 	printImpedance(data);
 
-	LBFGSpp::LBFGSParam<double> fitParam;
+	LBFGSpp::LBFGSParam<fvalue> fitParam;
 	fitParam.epsilon = 1e-6;
 	fitParam.max_iterations = 100;
 
-	LBFGSpp::LBFGSSolver<double> solver(fitParam);
+	LBFGSpp::LBFGSSolver<fvalue> solver(fitParam);
 	RtFunct funct(impedanceSpectra, aMatrixImag, aMatrixReal, 0.1, 0.001);
-	Eigen::VectorXd x = Eigen::VectorXd::Ones(4)*3;
-	double fx;
+	Eigen::VectorX<fvalue> x = Eigen::VectorX<fvalue>::Ones(4)*3;
+	fvalue fx;
 	int iterations = solver.minimize(funct, x, fx);
 
+	std::cout<<"Iterations: "<<iterations<<'\n';
+	std::cout<<"fx "<<fx<<'\n';
+	std::cout<<"xVect\n"<<x<<'\n';
+
 	return 0;
 }
-- 
GitLab