fsolve/fzero: No solution found, appears regular - matlab

I am trying to do the following algorithm using fsolve or fzero:
K5=8.37e-2
P=1
Choose an A
S2=(4*K5/A)^(2/3)
S6=3*S2
S8=4*S2
SO2 = (5*P)/149 - (101*S2)/149 - (293*S6)/149 - (389*S8)/149
H2O = (40*P)/149 + (556*S2)/447 + (636*S6)/149 + (2584*S8)/447
H2S = 2*SO2
newA = (H2O)^2/(SO2)^3
Repeat until newA=oldA
The main thing to solve is K5=1/4 * A * S2^3/2. It is from this that S2 is calculated in the first place.
So here is what I did in Matlab:
function MultipleNLEexample
clear, clc, format short g, format compact
Aguess = 300000; % initial guess
options = optimoptions('fsolve','Display','iter','TolFun',[1e-9],'TolX',[1e-9]); % Option to display output
xsolv=fsolve(#MNLEfun,Aguess,options);
[~,ans]=MNLEfun(xsolv)
%- - - - - - - - - - - - - - - - - - - - - -
function varargout = MNLEfun(A);
K5 = 8.37e-2;
S2 = (4*K5/A)^(2/3);
S6 = 3*S2;
S8 = 4*S2;
P=1; %atm
SO2 = (5*P)/149 - (101*S2)/149 - (293*S6)/149 - (389*S8)/149;
H2O = (40*P)/149 + (556*S2)/447 + (636*S6)/149 + (2584*S8)/447;
newA=H2O^2/SO2^3;
fx=1/4*newA*S2^(3/2)-K5;
varargout{1} = fx;
if nargout>1
H2S = 2*SO2;
varargout{2} = ((2*S2+6*S6+8*S8)/(2*S2+6*S6+8*S8+H2S+SO2)*100);
end
I cannot get my code to run, I get the following error:
No solution found.
fsolve stopped because the problem appears regular as measured by the gradient,
but the vector of function values is not near zero as measured by the
selected value of the function tolerance.
I have tried setting the tolerances as low as 1e-20 but that did not change anything.

The way your system is set up, it is actually convenient to plot it and observe its behavior. I vectorized your function and plottedf(x) = MLNEfun(x)-x, where the output of MLNE(x) is newA. Effectively, you are interested in a fixed point of your system.
What I observe is this:
There is a singularity, and a root crossing, at A ~ 3800. We can use fzero since it is a bracketed root solver, and give it very tight bounds on the solution fzero(#(x)MLNEfun(x)-x, [3824,3825]) which produces 3.8243e+03. This is a couple order of magnitudes from your starting guess. There is no solution to your system near ~3e5.
Update
In my haste, I failed to zoom in on the plot, which shows another (well-behaved) root at 1.3294e+04. It is up to you to decide which is the physically meaningful one(s). Everything I say below still applies. Just start your guess near the solution you're interested in.
In response to comment
Since you want to perform this for varying values of K, then your best bet is to stick with fzero so long as you are solving for one variable, instead of fsolve. The reason behind this is that fsolve uses variants of Newton's method, which are not bracketed and will struggle to find solutions at singular points like this. fzero on the other hand, uses Brent's method which is guaranteed to find a root (if it exists) within a bracket. It is also much more well behaved near singular points.
MATLAB's implementation of fzero also searches for a bracketing interval before carrying out Brent's method. So, if you provide a single starting guess sufficiently close to the root, it should find it for you. Sample output from fzero below:
fzero(#(x)MLNEfun(x)-x, 3000, optimset('display', 'iter'))
Search for an interval around 3000 containing a sign change:
Func-count a f(a) b f(b) Procedure
1 3000 -616789 3000 -616789 initial interval
3 2915.15 -433170 3084.85 -905801 search
5 2880 -377057 3120 -1.07362e+06 search
7 2830.29 -311972 3169.71 -1.38274e+06 search
9 2760 -241524 3240 -2.03722e+06 search
11 2660.59 -171701 3339.41 -3.80346e+06 search
13 2520 -109658 3480 -1.16164e+07 search
15 2321.18 -61340.4 3678.82 -1.7387e+08 search
17 2040 -29142.6 3960 2.52373e+08 search
Search for a zero in the interval [2040, 3960]:
Func-count x f(x) Procedure
17 2040 -29142.6 initial
18 2040.22 -29158.9 interpolation
19 3000.11 -617085 bisection
20 3480.06 -1.16224e+07 bisection
21 3960 2.52373e+08 bisection
22 3720.03 -4.83826e+08 interpolation
....
87 3824.32 -5.46204e+48 bisection
88 3824.32 1.03576e+50 bisection
89 3824.32 1.03576e+50 interpolation
Current point x may be near a singular point. The interval [2040, 3960]
reduced to the requested tolerance and the function changes sign in the interval,
but f(x) increased in magnitude as the interval reduced.
ans =
3.8243e+03

Related

Matlab - Chien Search implementation initialisation

I'm trying to implement a DVBS2 (48408, 48600) BCH decoder and I'm having troubles with finding the roots of the locator polynomial. For the Chien search here the author initialises the registers taking into account the fact that it is shortened subtracting 48600 from (2^16 - 1). Why so?
This is the code I have so far:
function [error_locations, errors] = compute_chien_search(n, locator_polynomial, field, alpha_powers, n_max)
t = length(locator_polynomial);
error_locations = zeros(t, 1);
errors = 0;
% Init the registers with the locator polynomial coefficients.
coefficient_buffer = locator_polynomial;
alpha_degrees = uint32(1:t)';
alpha_polynoms = field(alpha_degrees);
alpha_polynoms = [1; alpha_polynoms];
for i = 1:n %
for j = 2:t
coefficient_buffer(j) = gf_mul_elements(coefficient_buffer(j), ...
alpha_polynoms(j), ...
field, alpha_powers, n_max);
end
% Compute locator polynomial at position i
tmp = 0;
for j = 2:t
tmp = bitxor(tmp, coefficient_buffer(j));
end
% Signal the error
if tmp == 1
errors = errors + 1;
error_locations(errors) = n_max - i + 1;
end
end
end
It almost gives me the correct result except for some error locations. For example: for errors made in positions
418 14150 24575 25775 37403
The code above gives me
48183 47718 34451 24026 22826
which after subtracting from 48600 gives:
417 882 14149 24574 25774
which is the position minus 1, except for the 37403, which it did not find.
What am I missing?
Edit:
The code in question is a DVBS2 12 error correcting 48408, 48600 BCH code. The generator polynomial has degree 192 and is given by multiplying the 12 minimal polynomials given on the standard’s documentation.
Update - I created an example C program using Windows | Visual Studio for BCH(48600,48408). On my desktop (Intel 3770K 3.5 ghz, Win 7, VS 2015), encode takes about 30 us, a 12 bit error correction takes about 4.5 ms. On my laptop, (Intel Core i7-10510U up to 4.9 ghz, Win 10, VS 2019), 12 bit error correction takes about 3.0 ms. I used a carryless multiply intrinsic to simplify generating the 192 bit polynomial, but this is a one time generated constant. Encode uses a [256][3] 64 bit unsigned integer polynomial (192 bits) table and decode uses a [256][12] 16 bit unsigned integer syndrome table, to process a byte at a time.
The code includes both Berlekamp Massey and Sugiyama extended Euclid decoders that I copied from existing RS code I have. For BCH (not RS) code, the Berlekamp Massey discrepancy will be zero on odd steps, so for odd steps, the discrepancy is not calculated (the iteration count since last update is incremented, the same as when a calculated discrepancy is zero). I didn't see a significant change in running time, but I left the check in there.
The run times are about the same for BM or Euclid.
https://github.com/jeffareid/misc/blob/master/bch48600.c
I suspect an overflow problem in the case of a failure at bit error index 37403, since it is the only bit index > 2^15-1 (32767). There is this comment on that web site:
This code is great. However, it does not work for the large block sizes in the DVB-S2
specification. For example, it doesn't work with:
n = 16200;
n_max = 65535;
k_max = 65343;
t = 12;
prim_poly = 65581;
The good news is that the problem is easy to fix. Replace all the uint16() functions
in the code with uint32(). You will also have to run the following Matlab function
once. It took several hours for gftable() to complete on my computer.
gftable(16, 65581); (hex 1002D => x^16 + x^5 + x^3 + x^2 + 1)
The Chien search should be looking for values (1/(2^(0))) to (1/(2^(48599))), then zero - log of those values to get offsets relative to the right most bit of the message, and 48599-offset to get indexes relative to the left most bit of the message.
If the coefficients of the error locator polynomial are reversed, then the Chien search would be looking for values 2^(0) to 2^(48599).
https://en.wikipedia.org/wiki/Reciprocal_polynomial

Calculating a definite integral results in a really complex numerical expression instead of a numerical value

I am trying to calculate the percentage of sun radiation that is in the visible spectrum.
My code is:
h=6.626e-34; %planck constant
c=3e8; %speed of light
k=1.38066e-23; %boltzman constant
T1=5777; %temperature in surface of sun
syms f
J1 =(2*pi*h/(c^2))*((f^3)/(exp((h*f)/(k*T1))-1)); %planck radiation law
hold on;
ezplot(J1,[0,1.5e15])
a=int(J1,f,0,inf) %total energy radiated
b=int(J1,f,4e14,8e14) %energy between 400-800Thz (visible radiation spectrum)
Vp = (b/a)*100 %percentage of visible/total radiation
While the plot is exactly as it was expected to be, meaning i haven't totally messed it up, the result of the integrals is like this:
Vp = -1888568826285205004703258735621345426367059580820393216707788800000000000*((73396718487075910602267519716779133887030184268951416015625*log(1 - exp(23642358029674224853515625/7114894705749824515342336)))/205953985278202888163058116890711980917418253877248 - (73396718487075910602267519716779133887030184268951416015625*log(1 - exp(23642358029674224853515625/3557447352874912257671168)))/25744248159775361020382264611338997614677281734656 - (2390487322005187985890576650155251369405251302897930145263671875*polylog(2, exp(23642358029674224853515625/3557447352874912257671168)))/1857466834100924357302864708366291649120175241251782656 + (25952248522181378144831874533777514511468878135769288445192954296875*polylog(3, exp(23642358029674224853515625/3557447352874912257671168)))/67008813354583054015095330308295397033299967000474002915328 - (110058495767576691259417256590823904271799518678985866399923893187011219092083*polylog(4, exp(23642358029674224853515625/3557447352874912257671168)))/1888568826285205004703258735621345426367059580820393216707788800000000 + (2390487322005187985890576650155251369405251302897930145263671875*polylog(2, exp(23642358029674224853515625/7114894705749824515342336)))/7429867336403697429211458833465166596480700965007130624 - (25952248522181378144831874533777514511468878135769288445192954296875*polylog(3, exp(23642358029674224853515625/7114894705749824515342336)))/134017626709166108030190660616590794066599934000948005830656 + (110058495767576691259417256590823904271799518678985866399923893187011219092083*polylog(4, exp(23642358029674224853515625/7114894705749824515342336)))/1888568826285205004703258735621345426367059580820393216707788800000000 + 101409666798338314597227594049400067888200283050537109375/22835963083295358096932575511191922182123945984))/(12228721751952965695490806287869322696866613186553985155547099243001246565787*pi^4)
It is just a numerical expression (does not contain any constants) but i was expecting (and I am trying to find) a single value.
Any ideas how to overcome this?
Thanks in advance
double(Vp)
returns 44.3072 which is exactly what I was looking for while
vpa(Vp)
returns 44.307238264260285485868531074049 + 1.5008384323384489567473242679822e-35*i which is overcomed by
norm(vpa(Vp))
giving the same result as double(Vp)
However when i added a few more lines of code:
d=int(J1,f,0,4e14); %infrared energy
e=int(J1,f,8e14,inf); %ultraviolet energy
Ir = (d/a)*100; %percentage of infrared radiation
Uv = (e/a)*100; %percentage of ultraviolet radiation
double(Ir) gives this error:
Error using mupadmex
Error in MuPAD command: DOUBLE cannot convert the input expression into a double array.
If the input expression contains a symbolic variable, use the VPA function instead.
Error in sym/double (line 514)
Xstr = mupadmex('symobj::double', S.s, 0);
Error in symplanckradlaw (line 21)
Infrared=double(Ir)
While vpa(Ir) and even norm(vpa(Ir)) are given complicated complex numerical expressions like this
(abs(7.3340024900713941224341547032249e-56*limit((652255981594442743874468745505068648842285814001516463259648*f^2*polylog(2, exp((3873563939581825*f)/466281739436020499437475332096)))/15004497594028668398955870330625 - (608270107310811468411217054651916403272252179121058584901915330886243977872955782952124416*f*polylog(3, exp((3873563939581825*f)/466281739436020499437475332096)))/58120880811771703455030054556291506586990890625 - f^4/4 + (466281739436020499437475332096*f^3*log(1 - exp((3873563939581825*f)/466281739436020499437475332096)))/3873563939581825 + (283625243683820020974472587101002022201492492463545426687503953676410023626686775978867575742611811818507908158310055936*polylog(4, exp((3873563939581825*f)/466281739436020499437475332096)))/225134948049212098682315198853176286979563186266469146812890625, f == 0, Right) - 146.24549829953781858190522266202 - 2.501397387230748261245540446637e-36*i)^2)^(1/2)
You can convert it to a double using, well: double:
Vp_double = double(Vp)
You can also use vpa if you want to choose the precision:
Vp_vpa = vpa(Vp)
The reason why you get that immensely long expression is because MATLAB managed to find a "closed form definite integral". I.e. MATLAB managed to find an expression that accurately calculate the integral with no rounding errors. This is not always possible, and you'll get an error:
Warning: Explicit integral could not be found.
If that happens then you should try the approach given in this answer.
For some reason Matlab while was able to evaluate the limits in this integral
a=int(J1,f,0,inf)
it had problems evaluating limit to 0 in this integral
d=int(J1,f,0,4e14);
and limit to infinite in this integral
e=int(J1,f,8e14,inf);
I worked around this by putting instead of 0 a very low value (1e-45) and instead of infinite a very large one (1e22).
I got very good results for my purposes but I still find it really strange that matlab can evaluate the limits in one case but cannot evaluate the same limits in another case.

Developing a Secant Method Program for root finding

So I have been trying to develop a secant method program that can be used for finding the root of
f(x) = tanh(x) - (x / 3)
However the answer output is nowhere close. Every solution I have found seems a more complex way to solve it.
x = 2;
prevx = x;
for i = 1:20
x = x - (tanh(x)-(x/3))*((x-(prevx))/((tanh(x)-(x/3))-(tanh(prevx))-((prevx/3))));
prevx = prevx + x;
x
end
The answer should be 2.987. I am getting a negative number though for some reason.
You suppose to add up terms so replace minus in this line:
x = x - (tanh(x)-(x/3))*((x-(prevx))/((tanh(x)-(x/3))-(tanh(prevx))-((prevx/3))));
To a plus:
x = x + (tanh(x)-(x/3))*((x-(prevx))/((tanh(x)-(x/3))-(tanh(prevx))-((prevx/3))));
To get a desired result of 2.987 for x. Also remove x before the end of the loop.
Here is an alternative way to do it (#madbitloman is right on spot as for the error in your code).
The secant method can be illustrated as follows (from Wikipedia):
Now if we translate this into MATLAB code, that would look like this:
x(k) = x(k-1) - (f(x(k-1)))*((x(k-1) - x(k-2))/(f(x(k-1)) - f(x(k-2))));
What is left to be done is provide the algorithm with 2 initial estimates and some tolerance value to tell it when to stop searching for a root, i.e. when 2 consecutive entries in x are very close to each other.
Let's choose 2 and 4. The closer the estimates to the real root, the faster the convergence.
To avoid using tanh(x)-(x/3) inside the for loop, which could get messy, let's define an anonymous function that takes x as an argument:
f = #(x) tanh(x)-(x/3);
So that would be the f in the line of code above.
Therefore, the whole code would look like the following, with a tolerance of say 0.001:
clear
clc
%// Initial values and tolerance
x(1) = 2;
x(2) = 4;
f = #(x) tanh(x)-(x/3);
tolerance = 0.001;
%// Let's try from 3 to 15.
for k=3:15
x(k) = x(k-1) - (f(x(k-1)))*((x(k-1) - x(k-2))/(f(x(k-1)) - f(x(k-2))));
if abs(x(k)-x(k-1)) < tolerance
break
end
end
After running the code, x looks like this:
x =
2.0000 4.0000 2.9420 2.9839 2.9847
so it took 5 iterations to reach the desired tolerance. Note that due to floating arithmetic you might want to use a smaller tolerance because Matlab stores numbers with much more digits than 4.
Finally you can get the negative root as well using appropriate initial estimates like -2 and -4:
x =
-2.0000 -4.0000 -2.9420 -2.9839 -2.9847
Hope that helps somehow!

Matlab lsqnonlin() exitflag=4

I am optimizing some test data using lsqnonlin (i.e. data simulated from known parameter values).
maturity=[1 3 6 9 12 15 18 21 24 30 36 48 60 72 84 96 108 120]'; %maturities
options=optimset('Algorithm',{'levenberg-marquardt',.01},'Display','iter','TolFun',10^(-20),'TolX',10^-3,'MaxFunEvals',10000,'MaxIter',10000); %LM
vp0=[0.99 0.94 0.84 0.0802 -0.0144 -0.0042 0.001693 0.004094 0.003256 log(0.000960765^2) 0.077]'; %LM
[vpML,resnorm,residual,exitflag,output,lambda,jacobian]=lsqnonlin(#(vp) DNS_LL_LM(vp,y,maturity),vp0,[],[],options); %LM
I want the convergence to occur when the norm of the parameter vector changes by 10^-6.
As 'TolX' refers to the raw changes in the parameter vector I use 10^-3 as the tolerance of X which when squared would gives the desired norm of 10^-6.
However I find that when I run the code the exitflag keeps coming up as exitflag=4: "Magnitude of search direction was smaller than the specified tolerance."
But there is nowhere to set the tolerance for the search direction?
In the options you can only set: "TolX" and "TolFun"?
http://www.mathworks.co.uk/help/optim/ug/lsqnonlin.html#f265106
So how can I force the optimization to keep running till my desired convergence criterion?
Kind Regards
Baz
OK I went into the code and there seems to be some disconnect between what the exitflags as described here:
http://www.mathworks.co.uk/help/optim/ug/lsqnonlin.html#f265106
For example exitflag 2 which in the link above is supposed to relate to the change in x being less than tolerance is in fact used here to indicate that the Jacobian is undefined
if undefJac
EXITFLAG = 2;
msgFlag = 26;
msgData = {'levenbergMarquardt',msgFlag,verbosity > 0,detailedExitMsg,caller, ...
[], [], []};
done = true;
The description of exitflag 4 on the mathworks page is a little vague but you can see what it is doing below:
if norm(step) < tolX*(sqrtEps + norm(XOUT))
EXITFLAG = 4;
msgData = {'levenbergMarquardt',EXITFLAG,verbosity > 0,detailedExitMsg,caller, ...
norm(step)/(sqrtEps+norm(XOUT)),optionFeedback.TolX,tolX};
done = true;
Seems that it it testing if the norm of the stepsize is less than the tolerance of X times the norm of X. This is along the lines of what I want, and can easily be changed to give me exactly what I want.

Issue integrating inside ode45 loop

When I try to use this Matlab code it goes in an infinite loop. I am trying to perform integration inside ode45:
clear
clc
options = odeset('RelTol',1e-4,'AbsTol',[1e-4 1e-4 1e-5]);
[T,Y] = ode45(#rigid,[0 12],[0 1 1],options);
plot(T,Y(:,1),'+',T,Y(:,2),'*',T,Y(:,3),'.')
function dy = rigid(t,y)
dy = zeros(3,1); % a column vector
dy(1) = y(2) ;
dy(2) = -y(1) * y(3);
fun = #(t) exp(-t.^2).*log(t).^2+y(1);
q = integral(fun,0,Inf);
dy(3) = y(2) * y(3) + q;
There is no "infinite loop." Your function just takes a very long time to integrate. Try setting tspan to [0 1e-7]. It appears to be a high frequency oscillation, but I don't know if your equations are correct (that's a math question rather than a programming one). Such systems are hard to integrate accurately (ode15 might be a better choice), let alone quickly.
You also didn't bother to mention the important fact that the call to integral is generating a warning message:
Warning: Minimum step size reached near x = 1.75484e+22. There may be a
singularity, or the tolerances may be too tight for this problem.
> In funfun/private/integralCalc>checkSpacing at 457
In funfun/private/integralCalc>iterateScalarValued at 320
In funfun/private/integralCalc>vadapt at 133
In funfun/private/integralCalc at 84
In integral at 88
In rtest1>rigid at 17
In ode15s at 580
In rtest1 at 5
Printing out warning messages on each iteration greatly slows down integration. There's a good reason for this warning. You do realize that the function that you're evaluating with integral from 0 to Inf is equivalent to the following, right?
sqrt(pi)*((eulergamma + log(4))^2/8 + pi^2/16) + Inf*y(1)
where eulergamma is psi(1) or double(sym('eulergamma')). Your integrand doesn't converge.
If you like, can try to avoid the warning message in one of two ways.
1. Turn off the the warning (being sure to re-enable it afterwards). You can do that with the following code:
...
warning('OFF','MATLAB:integral:MinStepSize');
[T,Y] = ode45(#rigid,[0 12],[0 1 1],options);
warning('ON','MATLAB:integral:MinStepSize');
...
You can obtain the the ID for a waring via the lastwarn function.
2. The other option might be to change your integration bounds and avoid the warning altogether, e.g.:
...
q = integral(fun,0,1e20);
...
This may or may not be acceptable, but integral is not be returning the correct solution either because the result doesn't converge.