gagsd
asked on
Convert C++ to Excel VBA
I have a code in C++ which needs to be converted into VBA.
//#include <Rcpp.h>
//#include <math.h>
//#include <iostream.h>
using namespace Rcpp;
double qnormal_x,qnormal_y,qnormal_z;
double expq(double q, double w){
if(q==1.0){
return(exp(w));
}
else{
return (expl(log(1.0+(1.0-q)*w)/(1.0-q)));
}
}
double lnq(double q, double w){
if(q==1.0){
return(log(w));
}
else{
return ((exp(log(w)*(1.0-q))-1.0)/(1.0-q));
}
}
void setseed_qnormal(double v0, double z0){
qnormal_x = sqrt(1-v0*v0);
qnormal_y = v0;
qnormal_z = z0;
}
double Q8(double w, double v){
return(8*w*v*(((16.0*w*w-24.0)*w*w+10.0)*w*w-1.0));
}
double P8(double w){
return((((128.0*w*w-256.0)*w*w+160.0)*w*w-32.0)*w*w+1.0);
}
double f(double z){
return(1.0-fabs(1.0-1.99999*z));
}
void qnormal(double q){
double qq;
qnormal_y = Q8(qnormal_x,qnormal_y);
qnormal_x = P8(qnormal_x);
qq = (q+1.0)/(3.0-q);
qnormal_z = f(expq(qq,-qnormal_z*qnormal_z*0.5));
qnormal_z = sqrt(-2.0*lnq(qq,qnormal_z));
}
// [[Rcpp::export]]
NumericVector Chaotic(int n,double q,double v0, double z0){
double eta;
double xi;
NumericVector ux(n);
setseed_qnormal(v0,z0);
for(int i=0;i<n;i++){
qnormal(q);
xi = qnormal_x*qnormal_z;
eta = qnormal_y*qnormal_z;
ux[i]= xi;
}
return ux;
}
ASKER
I tried converting it as follows:
But, I am not getting the desired results. For e.g., for q=1.5 and with 10,000 iterations, it should generate a dataset with mean=0 and standard deviation=1.73. Reference .
Public Function Chaotic(Optional ByVal q As Double = 1#) As Double
If q > 3 Or q = 3 Then Exit Function
Dim u1 As Double, u2 As Double, qq As Double, z As Double
Dim qnormal_x As Double, qnormal_y As Double, qnormal_z As Double
u1 = Application.WorksheetFunction.NormInv(MT19937_RealNum3#(), 0, 1) 'v0
u2 = Application.WorksheetFunction.NormInv(MT19937_RealNum3#(), 0, 1) 'z0
qnormal_x = Sgn((1 - u1 * u1)) * Sqr(Abs(1 - u1 * u1))
qnormal_y = u1
qnormal_z = u2
qnormal_y = (8 * qnormal_x * qnormal_y * (((16 * qnormal_x * qnormal_x - 24) * qnormal_x * qnormal_x + 10) * qnormal_x * qnormal_x - 1))
qnormal_x = ((((128 * qnormal_x * qnormal_x - 256) * qnormal_x * qnormal_x + 160) * qnormal_x * qnormal_x - 32) * qnormal_x * qnormal_x + 1)
qq = (1 + q) / (3 - q)
qnormal_z = Sqr(-2 * qLOG(qq, (1 - Abs(1 - 1.99999 * (qEXP(qq, -qnormal_z * qnormal_z * 0.5))))))
Chaotic = qnormal_y * qnormal_z
End Function
Private Function qEXP(ByVal q As Double, ByVal w As Double) As Double
If q = 1 Then
qEXP = (Exp(w))
Else
qEXP = (Exp(Log(1 + (1 - q) * w) / (1 - q)))
End If
End Function
Private Function qLOG(ByVal q As Double, ByVal w As Double) As Double
If q = 1 Then
qLOG = (Log(w))
Else
qLOG = ((Exp(Log(w) * (1 - q)) - 1) / (1 - q))
End If
End Function
But, I am not getting the desired results. For e.g., for q=1.5 and with 10,000 iterations, it should generate a dataset with mean=0 and standard deviation=1.73. Reference .
ASKER
We can use RND() in place of MT19937_RealNum3#(). MT19937_RealNum3# is a separate function which is equivalent to RND() of VBA.
You did too much for the first step. Here we do just a 1:1 structural "translation". Thus every function and method in the original source gets its VBA counter-part.
Cause there are no such variables like u1, u2 in the original source.
The problem is: You cannot test the single functions and compare them right now. So start by translating this:
Write one function/method for each one in the above source, don't collapse/compact function calling. Cause this makes it harder to see, where your code works or behaves different. E.g. rounding could be different due to this.
Then you may wrap it up in an consuming function, where you pass-in some (randomized) values.
btw, using a different pseudo number generator than the built-in requires that you use a trustable one. Cause writing such a generator is pretty hard.
Cause there are no such variables like u1, u2 in the original source.
The problem is: You cannot test the single functions and compare them right now. So start by translating this:
double qnormal_x,qnormal_y,qnormal_z;
void setseed_qnormal(double v0, double z0){qnormal_x = sqrt(1-v0*v0);qnormal_y = v0;qnormal_z = z0;}
double expq(double q, double w){if(q==1.0){return(exp(w));}else{return (expl(log(1.0+(1.0-q)*w)/(1.0-q)));}}
double lnq(double q, double w){if(q==1.0){return(log(w));}else{return ((exp(log(w)*(1.0-q))-1.0)/(1.0-q));}}
double Q8(double w, double v){return(8*w*v*(((16.0*w*w-24.0)*w*w+10.0)*w*w-1.0));}
double P8(double w){return((((128.0*w*w-256.0)*w*w+160.0)*w*w-32.0)*w*w+1.0);}
double f(double z){return(1.0-fabs(1.0-1.99999*z));}
void qnormal(double q){
double qq;
qnormal_y = Q8(qnormal_x,qnormal_y);
qnormal_x = P8(qnormal_x);
qq = (q+1.0)/(3.0-q);
qnormal_z = f(expq(qq,-qnormal_z*qnormal_z*0.5));
qnormal_z = sqrt(-2.0*lnq(qq,qnormal_z));
}
NumericVector Chaotic(int n,double q,double v0, double z0){
double eta;
double xi;
NumericVector ux(n);
setseed_qnormal(v0,z0);
for(int i=0;i<n;i++){
qnormal(q);
xi = qnormal_x*qnormal_z;
eta = qnormal_y*qnormal_z;
ux[i]= xi;
}
return ux;
}
Write one function/method for each one in the above source, don't collapse/compact function calling. Cause this makes it harder to see, where your code works or behaves different. E.g. rounding could be different due to this.
Then you may wrap it up in an consuming function, where you pass-in some (randomized) values.
btw, using a different pseudo number generator than the built-in requires that you use a trustable one. Cause writing such a generator is pretty hard.
ASKER
I did that as well. But, the problem is C++ executes differently and I could not execute the last part on a standalone basis.
As per C++ qnormal(q) results in qnormal_x, qnormal_y, and qnormal_z. But when you call qnormal(q) in VBA it generates only one output. Hence I had to combine them. No other option.
NumericVector Chaotic(int n,double q,double v0, double z0){
double eta;
double xi;
NumericVector ux(n);
setseed_qnormal(v0,z0);
for(int i=0;i<n;i++){
qnormal(q);
xi = qnormal_x*qnormal_z;
eta = qnormal_y*qnormal_z;
ux[i]= xi;
}
return ux;
}
As per C++ qnormal(q) results in qnormal_x, qnormal_y, and qnormal_z. But when you call qnormal(q) in VBA it generates only one output. Hence I had to combine them. No other option.
Read the source again: qnormal does not have any output. It modifies global variables. Just do the same in your VBA code..
p.s. eta in the final method is not used.
Option Explicit
Private qnormal_x As Double
Private qnormal_y As Double
Private qnormal_z As Double
Private Sub setseed_qnormal(v0 As Double, z0 As Double)
qnormal_x = Math.Sqr(1 - v0 * v0)
qnormal_y = v0
qnormal_z = z0
End Sub
Private Function expq(q As Double, w As Double) As Double
If (q = 1#) Then
expq = Math.Exp(w)
Else
expq = Math.Exp(Log(1# + (1# - q) * w) / (1# - q)) ' Was expl, check precision.
End If
End Function
Private Function lnq(q As Double, w As Double) As Double
If (q = 1#) Then
lnq = Log(w)
Else
lnq = (Exp(Log(w) * (1# - q)) - 1#) / (1# - q)
End If
End Function
Private Function Q8(w As Double, v As Double) As Double
Q8 = (8# * w * v * (((16# * w * w - 24#) * w * w + 10#) * w * w - 1#))
End Function
Private Function P8(w As Double) As Double
P8 = ((((128# * w * w - 256#) * w * w + 160#) * w * w - 32#) * w * w + 1#)
End Function
Private Function f(z As Double) As Double
f = (1# - Math.Abs(1# - 1.99999 * z)) ' Was fabs, check precision.
End Function
Private Sub qnormal(q As Double)
Dim qq As Double
qnormal_y = Q8(qnormal_x, qnormal_y)
qnormal_x = P8(qnormal_x)
qq = (q + 1#) / (3# - q)
qnormal_z = f(expq(qq, -qnormal_z * qnormal_z * 0.5))
qnormal_z = Math.Sqr(-2# * lnq(qq, qnormal_z))
End Sub
Public Function Chaotic(n As Long, q As Double, v0 As Double, z0 As Double) As Double()
Dim eta As Double
Dim xi As Double
Dim ux() As Double
Dim i As Long
ReDim ux(0 To n - 1)
setseed_qnormal v0, z0
For i = 0 To n - 1
qnormal q
xi = qnormal_x * qnormal_z
eta = qnormal_y * qnormal_z
ux(i) = xi
Next i
Chaotic = ux
End Function
p.s. eta in the final method is not used.
Well, I would start by fixing the C++ source code. so many global variables that you don't really need ....
Next, identify what can be translated:
C Structures becomes user defined Types.
Void Functions becomes procédures (Sub)
Function returning value becomes function.
C++ structures and classes becomes classes.
References or pointers parameters becomes ByRef parameters.
Keep in mind that VBA support some objects concepts, such as interface inheritance.
With some efforts, you can even translate STL functionalities.
Next, identify what can be translated:
C Structures becomes user defined Types.
Void Functions becomes procédures (Sub)
Function returning value becomes function.
C++ structures and classes becomes classes.
References or pointers parameters becomes ByRef parameters.
Keep in mind that VBA support some objects concepts, such as interface inheritance.
With some efforts, you can even translate STL functionalities.
My only rewrite would be avoiding globals and using a type instead of three separate variables:
Option Explicit
Private Type VectorType
X As Double
Y As Double
Z As Double
End Type
Public Function Chaotic(n As Long, q As Double, v0 As Double, z0 As Double) As Double()
Dim qn As VectorType
Dim ux() As Double
Dim i As Long
ReDim ux(0 To n - 1)
SeedVector qn, v0, z0
For i = 0 To n - 1
CalculateVector qn, q
ux(i) = qn.X * qn.Z
Next i
Chaotic = ux
End Function
Private Sub CalculateVector(ByRef vector As VectorType, q As Double)
Dim qq As Double
vector.Y = Q8(vector.X, vector.Y)
vector.X = P8(vector.X)
qq = (q + 1#) / (3# - q)
vector.Z = F(ExpQ(qq, -vector.Z * vector.Z * 0.5))
vector.Z = Math.Sqr(-2# * LnQ(qq, vector.Z))
End Sub
Private Sub SeedVector(ByRef vector As VectorType, v0 As Double, z0 As Double)
vector.X = Math.Sqr(1 - v0 * v0)
vector.Y = v0
vector.Z = z0
End Sub
Private Function F(Z As Double) As Double
F = (1# - Math.Abs(1# - 1.99999 * Z)) ' Was fabs, check precision.
End Function
Private Function ExpQ(q As Double, w As Double) As Double
If (q = 1#) Then
ExpQ = Math.Exp(w)
Else
ExpQ = Math.Exp(Log(1# + (1# - q) * w) / (1# - q)) ' Was expl, check precision.
End If
End Function
Private Function LnQ(q As Double, w As Double) As Double
If (q = 1#) Then
LnQ = Log(w)
Else
LnQ = (Exp(Log(w) * (1# - q)) - 1#) / (1# - q)
End If
End Function
Private Function P8(w As Double) As Double
P8 = ((((128# * w * w - 256#) * w * w + 160#) * w * w - 32#) * w * w + 1#)
End Function
Private Function Q8(w As Double, v As Double) As Double
Q8 = (8# * w * v * (((16# * w * w - 24#) * w * w + 10#) * w * w - 1#))
End Function
ASKER
@ste5an,
I think I am missing something over there.
N in the original C++ code is the number of iterations (data items to be generated) and hence they would be part of a SUB in VBA and not of the function (Am I wrong here??).
Also, the output data being generated does not conform to the mean and stdev of the expected distribution as per the wikipedia page.
For, q=1.5 the mean = 0 and stdev=1.73.
Also, if I give a static input for v0 and z0 then my output has the same values.
What is the output mean and stdev. of the data (N=10000) when you are running it on C++ and VBA. I do not have a C++ compiler so not in a position to run C code.
I think I am missing something over there.
N in the original C++ code is the number of iterations (data items to be generated) and hence they would be part of a SUB in VBA and not of the function (Am I wrong here??).
Also, the output data being generated does not conform to the mean and stdev of the expected distribution as per the wikipedia page.
For, q=1.5 the mean = 0 and stdev=1.73.
Also, if I give a static input for v0 and z0 then my output has the same values.
Public Function Chaotic(n As Long, q As Double, v0 As Double, z0 As Double) As Double()
What is the output mean and stdev. of the data (N=10000) when you are running it on C++ and VBA. I do not have a C++ compiler so not in a position to run C code.
N in the original C++ code is the number of iterations (data items to be generated) and hence they would be part of a SUB in VBA and not of the function (Am I wrong here??).
- There is only one "data item" generated (returned), a NumericVector.
- There is a loop to generate a N-dimensional vector, but not an "iteration". Different concepts, even when both may use for() loops in code.
Also, the output data being generated does not conform to the mean and stdev of the expected distribution as per the wikipedia page.D'oh? Where does that come from? What to you mean (pun intended)? The result of that calculation is a single vector. Mean and deviation are statistical measures without sense in this case.
For, q=1.5 the mean = 0 and stdev=1.73.What do you mean ??
Also, if I give a static input for v0 and z0 then my output has the same values.
Simple tests in C++ in comparison to VBA show no difference. E.g.
Public Sub Test()
Dim v() As Double
v = Chaotic(3, 10, 0, 0)
Debug.Print v(2)
End Sub
returns ~0,881917 in both systems.
ASKER
See, this code is to generate Tsallis q-Gaussian Random Data. If we run 10,000 iterations of this the output is 10,000 different random numbers. Say if we output these numbers in Col. A, then for Range("A1:A10000") the mean and stdev. should match the mean and stdev as per the wikipedia page. For eg. for q=1.5 the mean=0 and stdev=(1 / ((1 / (3 - q)) * (5 - 3 * q))) ^ (1 / 2)=1.732050.
Hope this clarifies further.
Hope this clarifies further.
Well, the problem is quite simple: The original code has no random function. So either you omitted the important parts or you posted the wrong source.
This question needs an answer!
Become an EE member today
7 DAY FREE TRIALMembers can start a 7-Day Free trial then enjoy unlimited access to the platform.
View membership options
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
btw, what is your use-case? Cause making this a library could be the better way.