Returning variables Matlab and Python? - python

I am trying to translate some code from Matlab to Python. The start of my function (in Matlab) starts like this:
function [varargout]=hysime(varargin);
error(nargchk(3, 4, nargin))
if nargout > 2, error('too many output parameters'); end
verbose = 1; % default value
y = varargin{1}; % 1st parameter is the data set
Later in the program there is something like this:
n = varargin{2}; % the 2nd parameter is the noise
Rn = varargin{3}; % the 3rd parameter is the noise correlation matrix
if nargin == 4, verbose = ~strcmp(lower(varargin{4}),'off');end
and finally there is this:
varargout(1) = {kf};
if nargout == 2, varargout(2) = {Ek};end
return
I am incredibly confused about what this code means. On one hand I understand that the parameters inputed all need to be returned. However, at the end of the program it appears like only 2 variables will be returned (while we input 3?). How can I translate this code so that the necessary parameters are returned? I guess my biggest issue here is that the matlab code is letting the agent input the parameters however they do not all seem required? And it seems like there cannot be more than two outputs? I am just having trouble deciphering what each line of code here means. Can someone please provide an explanation both in what is happening with the matlab code and what I can do to make the same sort of functionality in python?
Thanks!

Short version
The MATLAB code should have been written like this:
function [kf, Ek]=hysime(y, n, Rn, verbose);
error(nargchk(3, 4, nargin))
if nargin < 4
verbose = true;
else
verbose = ~strcmpi(verbose ,'off');
end
If you know MATLAB, this should be clearer. The (mostly) corresponding Python code can be written like this:
def hysime(y, n, Rn, verbose=True):
# rest of the function here
return kf, Ek
Long Version
This function expects 3 or 4 input arguments, with a default value set if the fourth argument is not provided. However, it is done in a way that is much more complicated than it needs to be (although I see this pattern a lot). Ideally in MATLAB you would specify the same thing like this:
function [varargout]=hysime(y, n, Rn, verbose)
error(nargchk(3, 4, nargin))
if nargin < 4
verbose = true;
else
verbose = ~strcmpi(verbose ,'off');
end
So what it is doing is putting the first argument in the variable y, the second in the variable n, the third in Rn, and if the fourth is specified put it in verbose (based on how it compares to the string 'off'), otherwise set verbose to true.
In python, handling of optional arguments is built-in. You can specify default value for arguments right in the function definition by setting name=value, where name is the argument name and value is the default value. Also, for historical reasons MATLAB often uses 'on' and 'off' instead of true and false. This is almost never done in Python, people just use True and False. So you can specify the same thing in Python like so:
def hysime(y, n, Rn, verbose=True):
This makes y, n, and Rn required arguments, but lets verbose be optional.
This doesn't handle the case where verbose is set to the string 'off'. As I said, this is common in MATLAB for historical reasons, but you really shouldn't be doing it in MATLAB anymore when you can avoid it, and you definitely shouldn't be doing it in Python. But if you really, really need to handle that case, you can just do:
def hysime(y, n, Rn, verbose=True):
if isinstance(verbose, str):
verbose = verbose.lower() != 'off'
For the output, what the MATLAB code does is let the function either return one value or two. So someone could do this:
kf = hysime(y, n, Rn);
Or
[kf, Ek] = hysime(y, n, Rn);
However, this is also being done in a way that is more complicated than necessary. If you just return the two values, and only one return value is used, MATLAB will throw away the rest. So rather than messing around with varargout, the function could just return [kf, Ek] and MATLAB will do the same thing. An error will automatically occur if someone tries to call the function with more than 2 outputs as well, so that is also unnecessary.
Dealing with varargout in this way is only necessary if you want the program to change its behavior based on the number of outputs. In this sort of structure, the main reason you would do this is if eK was expensive to calculate, in which case you would do something like this:
varargout(1) = {kf};
if nargout == 2
# calculate eK here
varargout(2) = {Ek};
end
As for the return, that is only needed in MATLAB if you want to cause the function to stop early, before it reaches the end normally. Once it reaches the end normally, it will return automatically. So if the last block of code you specified is the end of the function, then the return is redundant.
So all the code you showed in MATLAB can be reduced to:
function [kf, Ek]=hysime(y, n, Rn, verbose);
error(nargchk(3, 4, nargin))
if nargin < 4
verbose = true;
else
verbose = ~strcmpi(verbose ,'off');
end
As for Python, as I mentioned in my other answer to you, Python requires all returned values be handled. It will never throw away returned values unless you explicitly tell it to. In your case, the simplest solution is just to have return kf, Ek at the end of the function. If someone wants both kf and Ek, they can do:
kf, Ek = hysime(y, n, Rn)
If they only want kf, they can do (where _ is the python convention for a variable you don't care about):
kf, _ = hysime(y, n, Rn)
Or:
kf = hysime(y, n, Rn)[0]
So the python code would be:
def hysime(y, n, Rn, verbose=True):
# rest of the function here
return kf, Ek
If you really want to be able to have one or two returned values, you would need to have another argument to change the behavior. Such as:
def hysime(y, n, Rn, verbose=True, ret_eK=False):
# rest of the function here
return (kf, Ek) if ret_eK else kf
So in this case if the ret_eK argument is specified and set to True, then kf, Ek is returned, otherwise just kf is returned. But in Python you really don't want to do this unless there is some major additional cost to calculating Ek, which doesn't appear to be in this case.

Related

How can I correspond multiple ‘values’ to a single key? (in a matrix diagonalization scenario)

I'm writing a code (using Python) to simultaneously diagonalize two commuting matrices A and B, and hoping to create a dictionary to do this. Here, 'keys' are the eigenvalues, and 'values' are eigenvectors (may or may not be degenerate). Here's the program I've written. However, it cannot give me all the eigenvectors with shared eigenvalue. I guess I need to change something in my code to accommodate degeneracy, but how can I do this? Thanks!
def simultaneous_eig(A, B):
epsilon = 10**-10
vals, vecs = la.eig(A)
degen = {}
for n in range(0,len(vals)):
for m in range(0,n):
#equality up to certain precision
if np.abs(vals[m]-vals[n]) < epsilon:
degen.get(vals[m], vecs[:,n])
degen[vals[n]] = np.array([vecs[:,n]])
return degen
I found a few issues in your function. First, to achieve the recursive comparison you are trying to make, the outer loop should go from 0 to len(vals)-1, and the inner loop should be from “n” to len(vals), as seen in my corrected snippet below.
Also, the “.get()” method for dictionaries does not modify in place. In other words, you must assign the output to a variable within the “if” clause (again, see corrected code below).
Finally, to avoid extra singleton dimensions, avoid putting “[]” around the variable you wish to convert to a numpy array.
The revised function below should solve your problems. I wasn’t able to test it completely, so let me know if there are still issues.
Happy coding!
def simultaneous_eig(A, B):
epsilon = 10**-10
vals, vecs = la.eig(A)
degen = {}
for n in range(0,len(vals)-1):
for m in range(n+1,len(vals)):
#equality up to certain precision
if np.abs(vals[m]-vals[n]) < epsilon:
vecs[:,n] = degen.get(vals[m])
degen[vals[n]] = np.array([vecs[:,n]])
return degen
Depending on the size of the values in your matrices, you may be using the wrong criterion for "almost equal". For example, when comparing scalars, 1E-20 and 1.1E-20 would be "almost equal" according to your criterion, but 1E20 and 1.1E20 would not be, even though the relative error is the same.
Why not just use math.isclose?

How to multiply without the * sign using recursion?

so as homework for a programming class on python we're supposed to multiply to integers (n,m) with each other WITHOUT using the * sign (or another multiplication form). We're supposed to use recursion to solve this problem, so i tried just adding n with itself, m number of times. I think my problem is with using recursion itself. I have searched on the internet for recursion usage, no results. Here is my code. Could someone point me in the right direction?
def mult(n,m):
""" mult outputs the product of two integers n and m
input: any numbers
"""
if m > 0:
return n + n
return m - 1
else:
return 1
I don't want to give you the answer to your homework here so instead hopefully I can provide an example of recursion that may help you along :-).
# Here we define a normal function in python
def count_down(val):
# Next we do some logic, in this case print the value
print(val)
# Now we check for some kind of "exit" condition. In our
# case we want the value to be greater than 1. If our value
# is less than one we do nothing, otherwise we call ourself
# with a new, different value.
if val > 1:
count_down(val-1)
count_down(5)
How can you apply this to what you're currently working on? Maybe, instead of printing something you could have it return something instead...
Thanks guys, i figured it out!!!
i had to return 0 instead of 1, otherwise the answer would always be one higher than what we wanted.
and i understand how you have to call upon the function, which is the main thing i missed.
Here's what i did:
def mult(n,m):
""" mult outputs the product of two integers n and m
input: any numbers
"""
if m == 0:
return 0
else:
return n + mult(n, m - 1)
You have the right mechanics, but you haven't internalized the basics you found in your searches. A recursive function usually breaks down to two cases:
Base Case --
How do you know when you're done? What do you want to do at that point?
Here, you've figured out that your base case is when the multiplier is 0. What do you want to return at this point? Remember, you're doing this as an additive process: I believe you want the additive identity element 0, not the multiplicative 1.
Recursion Case --
Do something trivial to simplify the problem, then recur with this simplified version.
Here, you've figured out that you want to enhance the running sum and reduce the multiplier by 1. However, you haven't called your function again. You haven't properly enhanced any sort of accumulative sum; you've doubled the multiplicand. Also, you're getting confused about recursion: return is to go back to whatever called this function. For recursion, you'll want something like
mult(n, m-1)
Now remember that this is a function: it returns a value. Now, what do you need to do with this value? For instance, if you're trying to compute 4*3, the statement above will give you the value of 4*2, What do you do with that, so that you can return the correct value of 4*3 to whatever called this instance? You'll want something like
result = mult(n, m-1)
return [...] result
... where you have to fill in that [...] spot. If you want, you can combine these into a single line of code; I'm just trying to make it easier for you.

Integrating a function with Python (sympy, quad) where the result is another function I want to plot

I want to integrate a function using python, where the output is a new function rather than a numerical value. For example, I have the equation (from Arnett 1982 -- analytical description of a supernova):
def A(z,tm,tni):
y=tm/(2*tni)
tm=8.8 # diffusion parameter
tni=8.77 # efolding time of Ni56
return 2*z*np.exp((-2*z*y)+(z**2))
I want to then find the integral of A, and then plot the results. First, I naively tried scipy.quad:
def Arnett(t,z,tm,tni,tco,Mni,Eni,Eco):
x=t/tm
Eni=3.90e+10 # Heating from Ni56 decay
Eco=6.78e+09 # Heating from Co56 decay
tni=8.77 # efolding time of Ni56
tco=111.3 # efolding time of Co56
tm=8.8 # diffusion parameter
f=integrate.quad(A(z,tm,tni),0,x) #integral of A
h=integrate.quad(B(z,tm,tni,tco),0,x) #integral of B
g=np.exp((-(x/tm)**2))
return Mni*g*((Eni-Eco)*f+Eco*h)
Where B is also a pre-defined function (not presented here). Both A and B are functions of z, however the final equation is a function of time, t. (I believe that it is herein I am causing my code to fail.)
The integrals of A and B run from zero to x, where x is a function of time t. Attempting to run the code as it stands gives me an error: "ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()".
So after a short search I thought that maybe sympy would be the way to go. However I am failing with this as well.
I wonder if anyone has a helpful suggestion how to complete this task please?
Many thanks,
Zach
You can integrate A analytically. Assuming I'm not missing something silly due to being up way too late, does the following help?
import sympy as sy
sys.displayhook = sy.pprint
A, y, z, tm, t, tni = sy.symbols('A, y, z, tm, t, tni')
A = 2*z*sy.exp(-2*z*y + z**2)
expr = sy.integrate(A, (z,0,t)) # patience - this takes a while
expr
# check:
(sy.diff(expr,t).simplify() - A.replace(z,t)).simplify()
# thus, the result:
expr.replace(y,tm/(2*tni)).replace(t,t/tm)
The last line yields the integral of your A function in analytic form, though it does require evaluating the imaginary error function (which you can do with scipy.special.erfi()).
I think what you are looking for are lambda expression (if i understood correctly what you said.. see here for extra information and some examples on lambda functions).
What they allow you to do is define an anonymous function in A and return it so that you get your B function, should work something like this:
def A(parameters):
return lambda x: x * parameters # for simplicity i applied a multiplication
# but you can apply anything you want to x
B = A(args)
x = B(2)
Hope I could provide you with a decent response!
I think the error you get comes from an incorrect call to scipy.integrate.quad:
The first argument needs to be just the function name, integration is then performed over the first variable of this function. The values of the other variables can be passed to the function via the args keyword.
The output of scipy.integrate.quad contains not only the value of the integral, but also an error estimate. So a tuple of 2 values is returned!
In the end the following function should work:
def Arnett(t, z, Mni, tm=8.8, tni=8.77, tco=111.3, Eni=3.90e+10,
Eco=6.78e+09):
x=t/tm
f,err=integrate.quad(A,0,x,args=(tm,tni)) #integral of A
h,err=integrate.quad(B,0,x,args=(tm,tni,tco)) #integral of B
g=np.exp((-(x/tm)**2))
return Mni*g*((Eni-Eco)*f+Eco*h)
But an even better solution would probably be integrating A and B analytically and then evaluating the expression as murison suggested.

How to execute only parts of a function?

I have defined a class which contains some basic matrix functions. My function for transposing a matrix looks like this:
def transpose(self):
'''
Transpose a matrix.
'''
C = Z ## creating a new zero matrix
for i in range(0, self.rows):
for j in range(0, self.cols):
C.matrix[i][j] = self.matrix[j][i]
## printing the resultant matrix
C.show()
return C
So when I call this function from the interpreter, it prints the result after execution (because of the show() function).
However, when I call this function from another function in the same class, I don't want the matrix to be printed, that is, I don't want the C.show() part to execute.
Is there any way to do this? I was thinking on the lines of __name__ == "__main__" but that doesn't really apply here it seems.
Just add another, default, parameter to the function and put the print in an if:
def transpose(self, print_matrix=True):
'''
Transpose a matrix.
'''
C = Z ## creating a new zero matrix
for i in range(0, self.rows):
for j in range(0, self.cols):
C.matrix[i][j] = self.matrix[j][i]
## printing the resultant matrix
if print_matrix:
C.show()
return C
As it has a default value you don't need to change any current method calls, but you can add another parameter to your new one. Call it as transpose(False), if you don't want to print.
The problem you have is that the calculation and display are both coupled into the same function. In general, tight coupling like this is considered undesirable. Why? Well, you are seeing the problem now, you can't do one part of the function without the other.
Now I could give you crazy answers about how to only print when called from the interpreter, but I would be encouraging bad code. Instead, we should decouple this function into two different function.
Decoupling is simple, take the two different things your code is doing -- printing and calculating -- and separate them into two different functions.
def transpose(self):
'''
Transpose a matrix.
'''
C = Z ## creating a new zero matrix
for i in range(0, self.rows):
for j in range(0, self.cols):
C.matrix[i][j] = self.matrix[j][i]
return C
def transposeAndPrint(self):
C = transpose(self)
C.show()
Now you can call transposeAndPrint when you need to print, and transpose when you don't need to.
Of course I could tell you to add an extra parameter but that won't change the fact that a transpose function should not do printing (except for debug).
def transpose(self):
'''
Transpose a matrix.
'''
C = Z ## creating a new zero matrix
for i in range(0, self.rows):
for j in range(0, self.cols):
C.matrix[i][j] = self.matrix[j][i]
return C
And somewhere else, where you need it:
o.transpose()
o.show()
Another option would be to use Python's logging module. You can set multiple logging levels, which are intended for just this type of situation. In this case, you could make the show function output at the DEBUG level, which you can then easily turn on or off as need be.
See:
http://docs.python.org/2/howto/logging.html

what's the reason for the unexpect behavior of plot()

def f(u):
value = 0.0
if u > -1 and u < 1:
value = u * u
return value
Given the above, the following produces the expected plot:
plot(f,(x,-5,5))
But plot(f(x),(x,-5,5)) just draws a horizontal line. Can anyone explain what's going on?
The former passes the function, allowing it to be called inside plot(). The latter calls the function once and passes the returned value, resulting in the same value each time.
Similar to what #Ignacio said, the cause is the function being called once. The problem with this vs other functions like sin is the conditional. The if statement is evaluated when the function is called and not preserved as a symbolic statement. That is, the u > -1 and u < 1[1] is evaluated on the first function call and result is treated accordingly (i.e. left at 0).
As an illustration of what is happening:
sage: x = var('x')
sage: print ":)" if x > 0 else ":("
:(
There is no way to get around this in general[2], because Python has to evaluate the condition in the if statement to work out which code path to take when the function is called.
Best case solution
There is a solution that should work (but doesn't yet). Sage provides Piecewise, so you can define f as:
f = Piecewise([((-5, -1), ConstantFunction(0)),
((-1, 1), x*x),
((1, 5), ConstantFunction(0))],
x)
Unfortunately, the implementation of Piecewise is as yet incomplete, and severely lacking, so the only way to plot this seems to be:
f.plot()
(Limitations: trying to call f with a variable causes errors; it doesn't work with the conventional plot; you can't restrict the domain in Piecewise.plot, it plots the whole thing (hence why I restricted it to &pm;5); it doesn't cope with infinite intervals.)
Working solution
You could also just detect whether the argument to f is a number or variable and do the appropriate action based on that:
def f(u):
try:
float(u) # see it's a number by trying to convert
return u*u if -1 < u < 1 else 0.0
except TypeError: # the conversion failed
if callable(f):
return lambda uu: f(u(uu))
else:
return f
Note the callable call, it checks to see if u is a function (in some sense), if so returns the composition of f with u.
This version allows us to do things like:
sage: f(10)
0.0
sage: f(x)(0.5)
0.25
sage: f(x+3)(-2.2)
0.64
and it also works perfectly fine with plot, in either form. (Although it warns about DeprecationWarnings because of the u(uu) syntax; there are ways to get around this using u.variables but they are fairly awkward.)
Note: This "working" solution is quite fragile, and very suboptimal; the Piecewise version would be the correct solution, if it worked.
[1]: Python actually allows you to write this as -1 < u < 1. Pretty cool.
[2]: Although in some special cases you can, e.g. if you know x > 0, then you can use assume(x > 0) which means the example will print :).
Here is a (possibly) simpler solution for now, using lambdas.
sage: plot(lambda x:f(x), (x,-5,5))

Categories

Resources