Hi I am trying to create a function that takes as input a few parameters (irrelevant to the variables optimized) and a variable over which I want to optimize. For example for foo_function(a,b,c,x) I want to pass a,b,c as fixed parameters to foo_function, and finding the minimum of foo_function by changing x. If I was able to work in Matlab or Python this should be relatively easy as a function can return a handle to another function, but that's not possible in VBA. Im trying to use the code in http://www.quantcode.com/modules/mydownloads/singlefile.php?lid=424
Does anybody have any idea how to proceed?
You can declare foo_function to take a parameter array, treat it as a function of 1 variable if only 1 parameter is passed, and if more than 1 is passed, you can store the parameters in static variables. This allows your calling code to set the parameters of foo_function before passing its name to optimization code which will treat it as a function of 1 variable.
As a proof of concept, the following function represents a quadratic function:
Function quad(ParamArray args()) As Double
Dim x As Double
Static a As Double
Static b As Double
Static c As Double
If UBound(args) = 0 Then
x = args(0)
Else 'assumes that at least 3 parameters passed
a = args(0)
b = args(1)
c = args(2)
If UBound(args) = 3 Then
x = args(3)
Else
Exit Function 'function call just initializes statics
End If
End If
quad = a * x ^ 2 + b * x + c
End Function
When just 1 is passed, quad(x) just evaluates ax^2 + bx + c with its current values for those coefficients. If 4 is passed then it is interpreted as a call to quad(a,b,c,x), with the obvious meaning. If just 3 parameters are passed, it is called like a sub:
quad a,b,c
and doesn't return anything but sets the static parameters to the passed values.
To test it, I wrote a crude numerical differentiation function:
Function Derivative(f As String, x As Double, h As Double) As Double
Derivative = (Application.Run(f, x + h) - Application.Run(f, x)) / h
End Function
This approximates the derivative of f at the given x value using the given step size.
The following test sub shows how quad can be set before passing it the the derivative function:
Sub test()
quad 1, 2, -3 'initializes quad to be x^2 + 2x - 3
Debug.Print Derivative("quad", 2, 0.0001) 'should be approximately 6
quad 3, 2, 1
Debug.Print Derivative("quad", 2, 0.0001) 'now around 14
End Sub
Output:
6.00010000001205
14.0003000000277
Another, in some ways simpler, approach is to use public variables instead of actual parameters for the parameters to foo_function that are irrelevant to the variables being optimized. Then the calling code could assign to these public variables before optimizing. This approach would eliminate the need for parameter arrays but has the drawback of using global-type variables which tend to make programs not sufficiently modular.
Related
This code is supposed to generate a sequence of 10,000 random numbers in VBA. For some reason I am only able to produce a unique sequence of length 5842, and then it repeats. But, and this is the strangest part, each time I run the code, the sequence starts in a different place. For example in one run, the elements following element 2660 are the same as those following element 8502 (8502-2660= 5842). The next run, I get a sequence that repeats following elements 3704 and 9546 (9546-3704=5842). And so on.
Function NormRand() As Double
' NormRand returns a randomly distributed drawing from a
' standard normal distribution i.e. one with:
' Average = 0 and Standard Deviation = 1.0
Dim fac As Double, rsq As Double, v1 As Double, v2 As Double
Static flag As Boolean, gset As Double
' Each pass through the calculation of the routine produces
' two normally-distributed deviates, so we only need to do
' the calculations every other call. So we set the flag
' variable (to true) if gset contains a spare NormRand value.
If flag Then
NormRand = gset
' Force calculation next time.
flag = False
Else
' Don't have anything saved so need to find a pair of values
' First generate a co-ordinate pair within the unit circle:
Do
v1 = 2 * Rnd - 1#
v2 = 2 * Rnd - 1#
rsq = v1 * v1 + v2 * v2
Loop Until rsq <= 1#
' Do the Math:
fac = Sqr(-2# * Log(rsq) / rsq)
' Return one of the values and save the other (gset) for next time:
NormRand = v2 * fac
gset = v1 * fac
flag = True
End If
End Function
For some reason I am only able to produce a unique sequence of length
5842, and then it repeats. But, and this is the strangest part, each
time I run the code, the sequence starts in a different place
That's by design and well known - that's why the number generation is labelled pseudo random and not random.
By the way, I notice that you are multiplying the two values. That may not be a good idea - as mentioned here.
In your function, you may try to replace Rnd with RndDbl:
Public Function RndDbl(Optional ByRef Number As Single) As Double
' Exponent to shift the significant digits of a single to
' the least significant digits of a double.
Const Exponent As Long = 7
Dim Value As Double
' Generate two values like:
' 0.1851513
' 0.000000072890967130661
' and add these.
Value = CDbl(Rnd(Number)) + CDbl(Rnd(Number) * 10 ^ -Exponent)
RndDbl = Value
End Function
and then modify your code to include a dynamic seed by calling Timer:
Do
v1 = 2 * RndDbl(-Timer) - 1#
v2 = 2 * RndDbl(-Timer) - 1#
rsq = v1 + v2
Loop Until rsq <= 1#
The generated values will still not be true random, but should not take form of a repeated sequence.
In the below example code from a tutorial, I understand how the majority of it works. There are a few things I do not understand, though. Please help me to understand the purpose of some of these characters and the purpose of them. I will explain below what I don't understand.
Module paramByref
Sub swap(ByRef x As Integer, ByRef y As Integer)
Dim temp As Integer
temp = x ' save the value of x
x = y ' put y into x
y = temp 'put temp into y
End Sub
Sub Main()
' local variable definition
Dim a As Integer = 100
Dim b As Integer = 200
Console.WriteLine("Before swap, value of a : {0}", a)
Console.WriteLine("Before swap, value of b : {0}", b)
' calling a function to swap the values '
swap(a, b)
Console.WriteLine("After swap, value of a : {0}", a)
Console.WriteLine("After swap, value of b : {0}", b)
Console.ReadLine()
End Sub
End Module
What I don't understand is why the need for x, y, and temp. why not just keep declaration of a and b and swap the values?
Also, on the Console.WriteLine, I understand the {0} is an nth reference or index position, but the comma suggests to output something else. Is it saying to output the value of a in the 0 index? If so, there can only be one zero index, so why does the next line reference the value of the zero index? Or am I all wrong on this? Any explanation would be greatly appreciated...please dumb your answer down for this newbie.
The reason for x, y, and temp is to demonstrate how to do swapping. Your question asks
why not just keep declaration of a and b and swap the values
In this situation where you aren't doing any incrementing, sure you could just afterward say b = 100 and a = 200 but if you were doing this say in a loop and constantly swapping them, if you were to do
a = b
b = a
you would have a=100, b=100 because you set the value of b = a which is 100, then set a as the same value so it is still 100. That's why you need the temp value.
With regards to the console.writeLine, you are absolutely correct that it is indexing the 0th index, so in the first example, it is a, however it can be reused in the next line, because it is a totally separate line of code. Each of those console.writeLines can exist independently, so the values are indexed on a line by line basis
You need a temp to store the value from the first var so that you do not lose it when you overwrite it with the value from the second var. Once the second var's value has been transferred into the first var, the value stored in temp (from the original first var) can be transferred into the second var (overwriting the original value).
This is a common method when creating custom sorting routines that shuffle the values from various positions and ranks in an array around.
a and b are variables that are only visible in method Main(). They cannot be accessed from a different method. On the other hand, x, y, and temp are variables that are only visible in method swap(). More precisely, x and y are the method's parameters. The keyword ByRef means that the parameters are references to variables. I.e. when you call swap(a, b), x becomes a reference to variable a and y becomes a reference to variable b. You can work with references like with other variables. The difference is that when you change x in swap(), a in Main will change accordingly.
The Console.WriteLine() method takes a string parameter and an arbitrary number of additional parameters. The {0} is an index into the list of those additional parameters. E.g. Console.WriteLine("{0} {1} {2}", 100, 200, 300) would output 100 200 300. and Console.WriteLine("{2} {1} {0}", 100, 200, 300) would output 300 200 100.
I am trying to follow the tutorial of using the optimization tool box in MATLAB. Specifically, I have a function
f = exp(x(1))*(4*x(1)^2+2*x(2)^2+4*x(1)*x(2)+2*x(2)+1)+b
subject to the constraint:
(x(1))^2+x(2)-1=0,
-x(1)*x(2)-10<=0.
and I want to minimize this function for a range of b=[0,20]. (That is, I want to minimize this function for b=0, b=1,b=2 ... and so on).
Below is the steps taken from the MATLAB's tutorial webpage(http://www.mathworks.com/help/optim/ug/nonlinear-equality-and-inequality-constraints.html), how should I change the code so that, the optimization will run for 20 times, and save the optimal values for each b?
Step 1: Write a file objfun.m.
function f = objfun(x)
f = exp(x(1))*(4*x(1)^2+2*x(2)^2+4*x(1)*x(2)+2*x(2)+1)+b;
Step 2: Write a file confuneq.m for the nonlinear constraints.
function [c, ceq] = confuneq(x)
% Nonlinear inequality constraints
c = -x(1)*x(2) - 10;
% Nonlinear equality constraints
ceq = x(1)^2 + x(2) - 1;
Step 3: Invoke constrained optimization routine.
x0 = [-1,1]; % Make a starting guess at the solution
options = optimoptions(#fmincon,'Algorithm','sqp');
[x,fval] = fmincon(#objfun,x0,[],[],[],[],[],[],...
#confuneq,options);
After 21 function evaluations, the solution produced is
x, fval
x =
-0.7529 0.4332
fval =
1.5093
Update:
I tried your answer, but I am encountering problem with your step 2. Bascially, I just fill the my step 2 to your step 2 (below the comment "optimization just like before").
%initialize list of targets
b = 0:1:20;
%preallocate/initialize result vectors using zeros (increases speed)
opt_x = zeros(length(b));
opt_fval = zeros(length(b));
>> for idx = 1, length(b)
objfun = #(x)objfun_builder(x,b)
%optimization just like before
x0 = [-1,1]; % Make a starting guess at the solution
options = optimoptions(#fmincon,'Algorithm','sqp');
[x,fval] = fmincon(#objfun,x0,[],[],[],[],[],[],...
#confuneq,options);
%end the stuff I fill in
opt_x(idx) = x
opt_fval(idx) = fval
end
However, it gave me the output is:
Error: "objfun" was previously used as a variable, conflicting
with its use here as the name of a function or command.
See "How MATLAB Recognizes Command Syntax" in the MATLAB
documentation for details.
There are two things you need to change about your code:
Creation of the objective function.
Multiple optimizations using a loop.
1st Step
For more flexibility with regard to b, you need to set up another function that returns a handle to the desired objective function, e.g.
function h = objfun_builder(x, b)
h = #(x)(objfun(x));
function f = objfun(x)
f = exp(x(1))*(4*x(1)^2+2*x(2)^2+4*x(1)*x(2)+2*x(2)+1) + b;
end
end
A more elegant and shorter approach are anonymous functions, e.g.
objfun_builder = #(x,b)(exp(x(1))*(4*x(1)^2+2*x(2)^2+4*x(1)*x(2)+2*x(2)+1) + b);
After all, this works out to be the same as above. It might be less intuitive for a Matlab-beginner, though.
2nd Step
Instead of placing an .m-file objfun.m in your path, you will need to call
objfun = #(x)(objfun_builder(x,myB));
to create an objective function in your workspace. In order to loop over the interval b=[0,20], use the following loop
%initialize list of targets
b = 0:1:20;
%preallocate/initialize result vectors using zeros (increases speed)
opt_x = zeros(length(b))
opt_fval = zeros(length(b))
%start optimization of list of targets (`b`s)
for idx = 1, length(b)
objfun = #(x)objfun_builder(x,b)
%optimization just like before
opt_x(idx) = x
opt_fval(idx) = fval
end
How do I use Excel VBA to find the minimum value of an equation?
For example, if I have the equation y = 2x^2 + 14, and I want to make a loop that will slowly increase/decrease the value of x until it can find the smallest value possible for y, and then let me know what the corresponding value of x is, how would I go about doing that?
Is there a method that would work for much more complicated equations?
Thank you for your help!
Edit: more details
I'm trying to design a program that will find a certain constant needed to graph a nuclear decay. This constant is a part of an equation that gets me a calculated decay. I'm comparing this calculated decay against a measured decay. However, the constant changes very slightly as the decay happens, which means I have to use something called a residual-square to find the best constant to use that will fit the entire decay best to make my calculated decay as accurate as possible.
It works by doing (Measured Decay - Calculated Decay) ^2
You do that for the decay at several times, and add them all up. What I need my program to do is to slowly increase and decrease this constant until I can find a minimum value for the value I get when I add up the residual-squared results for all the times using this decay. The residual-squared that has the smallest value has the value of the constant that I want.
I already drafted a program that does all the calculations and such. I'm just not sure how to find this minimum value. I'm sure if a method works for something like y = x^2 + 1, I can adapt it to work for my needs.
Test the output while looping to look for the smallest output result.
Here's an Example:
Sub FormulaLoop()
Dim x As Double
Dim y As Double
Dim yBest As Double
x = 1
y = (x ^ 2) + 14
yBest = y
For x = 2 To 100
y = (x ^ 2) + 14
If y < yBest Then
yBest = y
End If
Next x
MsgBox "The smallest output of y was: " & yBest
End Sub
If you want to loop through all the possibilities of two variables that make up x then I'd recommend looping in this format:
Sub FormulaLoop_v2()
Dim MeasuredDecay As Double
Dim CalculatedDecay As Double
Dim y As Double
Dim yBest As Double
MeasuredDecay = 1
CalculatedDecay = 1
y = ((MeasuredDecay - CalculatedDecay) ^ 2) + 14
yBest = y
For MeasuredDecay = 2 To 100
For CalculatedDecay = 2 To 100
y = ((MeasuredDecay - CalculatedDecay) ^ 2) + 14
If y < yBest Then
yBest = y
End If
Next CalculatedDecay
Next MeasuredDecay
MsgBox "The smallest output of y was: " & yBest
End Sub
I have the following code for finding factorials:
Private Shared Function Factorial(ByVal Number As Long) As Long
If Number = 0 Then
Return 1
Else
Return Number * Factorial(Number - 1)
End If
End Function
It usually results in an overflow. It only works if I start with something small like 4.
I have to work with starting numbers such as 30-60.
Any ideas? I thought changing the value type to LONG would prevent this problem.
This is VB.net just for reference.
Factorials get very large, very quickly. The largest number that will fit in a Long is about 9×10^18. Factorial(30) is about 2.7×10^32.
If you're using .Net 4 there is a built-in BigInteger class that you can use which will hold arbitrarily large numbers.
If you're not using .Net 4, you'll need to find and download a BigInteger library, for example intx.
You get the overflow exception only with integer and long types. To avoid that, you can use System.Double or System.Numerics.BigInteger (or BigDecimal I think).
For example,
if you run 3 different versions of the factorial: 1 with long, 1 with double and 1 with biginteger as follow with a range of values of 5 to 50 by 5:
'Long Factorial
Public Function FactorialInt64(ByVal n As Integer) As Int64
If n = 1 Then
Return 1
Else
Return n * FactorialInt64(n - 1)
End If
End Function
' Double Factorial
Public Function FactorialDouble(ByVal n As Integer) As Double
If n = 1 Then
Return 1
Else
Return n * FactorialDouble(n - 1)
End If
End Function
' BigInteger Factorial
Public Function FactorialBigInteger(ByVal n As Integer) As BigInteger
If n = 1 Then
Return 1
Else
Return n * FactorialBigInteger(n - 1)
End If
End Function
You will get a result like this:
You can find the complete source code in my blog post: Factorial and Fibonacci in VB.NET
There is a big int Library for .NET that will solve your problem. It can manipulate very large number (limited only by your system memory).
Here is the link: http://www.emilstefanov.net/Projects/GnuMpDotNet/