The lambda function does not work with the scipy integral in python - python

I have the code structure below.
import numpy as np
from scipy import integrate
t = np.linspace(0, .85, 5)
s = np.ones_like(t)
f = lambda t, s: t - s
Int = integrate.quad(f, 1, 2)
Int
Even if I change the s and the bounds in (f, 1, 2), I received the error below. I compared this to the information provided by the Scipy and Lambda references. Still, I cannot see how I can solve this error.
TypeError Traceback (most recent call last)
<ipython-input-6-087d4b0d784a> in <module>
4 s = np.ones_like(t)
5 f = lambda t, s: t - s
----> 6 Int = integrate.quad(f, 1, 2)
7 Int
1 frames
/usr/local/lib/python3.8/dist-packages/scipy/integrate/quadpack.py in _quad(func, a, b, args, full_output, epsabs, epsrel, limit, points)
461 if points is None:
462 if infbounds == 0:
--> 463 return _quadpack._qagse(func,a,b,args,full_output,epsabs,epsrel,limit)
464 else:
465 return _quadpack._qagie(func,bound,infbounds,args,full_output,epsabs,epsrel,limit)
TypeError: <lambda>() missing 1 required positional argument: 's'
EDIT: I'm trying to code the following integral, but in this post I tried to express it more simply.
integral

Here is an answer that will only resolve the mentioned problem:
s = np.ones_like(t)
f = lambda t, s: t - s
Here, the lambda's s doesn't capture the variable s you declared before, it is just a second parameter that you will need to fill in at some point. So .quad function expect either a function with one parameter, or if it has more than one, expect the rest of the arguments to be filled in the .quad's args parameter. (https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html)
This is why the error tells you
missing 1 required positional argument: 's'
If you want to capture a previously declared variable you don't need to specify another parameter, so your lambda should be:
f = lambda t: t - s
With this, you don't need to fill in additional arguments in the args parameter and you will have no error about the number of arguments
If you wanted to capture the t variable from before, you need to remove the parameter in the lambda too but then you'll end up with a lambda that takes 0 arguments, and .quad takes a lambda/function that takes at least one parameter

Related

Can a function be one of kwargs in Python partial?

I would like to use functools.partial to reduce the number of arguments in one of my functions. Here's the catch: one or more kwargs may be functions themselves. Here's what I mean:
from functools import partial
def B(alpha, x, y):
return alpha(x)*y
def alpha(x):
return x+1
g = partial(B, alpha=alpha, y=2)
print(g(5))
This throws an error:
TypeError: B() got multiple values for argument 'alpha'
Can partial handle functions as provided arguments? If not is there a workaround or something more generic than partial?
partial itself doesn't know that a given positional argument should be assigned to x just because you specified a keyword argument for alpha. If you want alpha to be particular function, pass that function as a positional argument to partial.
>>> g = partial(B, alpha, y=2)
>>> g(5)
12
g is equivalent to
def g(x):
return alpha(x) * 2 # == (x + 1) * 2
Alternately, you can use your original definition of g, but be sure to pass 5 as a keyword argument as well, avoiding any additional positional arguments.
>>> g = partial(B, alpha=alpha, y=2)
>>> g(x=5)
12
This works because between g and partial, you have provided keyword arguments for all required parameters, eliminating the need for any positional arguments.

How to specify arg position for functool partial()

As per manual, functools partial() is 'used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.'
What's the best way to specify the positions of the arguments that one wishes to evaluate?
EDIT
Note as per comments, the function to be partially evaluated may contain named and unnamed arguments (these functions should be completely arbitrary and may be preexisting)
END EDIT
For example, consider:
def f(x,y,z):
return x + 2*y + 3*z
Then, using
from functools import partial
both
partial(f,4)(5,6)
and
partial(f,4,5)(6)
give 32.
But what if one wants to evaluate, say the third argument z or the first and third arguments x, and z?
Is there a convenient way to pass the position information to partial, using a decorator or a dict whose keys are the desired arg positions and the respective values are the arg values? eg to pass the x and z positions something like like this:
partial_dict(f,{0:4,2:6})(5)
No, partial is not designed to freeze positional arguments at non-sequential positions.
To achieve the desired behavior outlined in your question, you would have to come up with a wrapper function of your own like this:
def partial_positionals(func, positionals, **keywords):
def wrapper(*args, **kwargs):
arg = iter(args)
return func(*(positionals[i] if i in positionals else next(arg)
for i in range(len(args) + len(positionals))), **{**keywords, **kwargs})
return wrapper
so that:
def f(x, y, z):
return x + 2 * y + 3 * z
print(partial_positionals(f, {0: 4, 2: 6})(5))
outputs:
32
Simply use keyword arguments. Using your definition of f above,
>>> g = partial(f, z=10)
>>> g(2, 4)
40
>>> h = partial(f, y=4, z=10)
>>> h(2)
40
Note that once you use a keyword argument for a given parameter, you must use keyword arguments for all remaining arguments. For example, the following would not be valid:
>>> j = partial(f, x=2, z=10)
>>> j(4)
TypeError: f() got multiple values for argument 'x'
But continuing to use keyword arguments is:
>>> j = partial(f, x=2, z=10)
>>> j(y=4)
40
When you use functools.partial, you store the values of *args and **kwargs for later interpolation. When you later call the "partially applied" function, the implementation of functools.partial effectively adds the previously provided *args and **kwargs to the argument list at the front and end, respectively, as though you had inserted these argument-unpackings yourself. I.e., calling
h = partial(1, z=10)
f(4)
is roughly equivalent to writing
args = [1]
kwargs = {'z': 10}
f(*args, 4, **kwargs)
As such, the semantics of how you provide arguments to functools.partial is the same as how you would need to store arguments in the args and kwargs variables above such that the final call to f is sensible. For more information, take a look at the pseduo-implementation of functools.partial given in the functools module documentation
For easier usage, you can create a new object specifically to specify a positional argument that is to be skipped when sequentially listing values for positional arguments to be frozen with partial:
SKIP = object()
def partial_positionals(func, *positionals, **keywords):
def wrapper(*args, **kwargs):
arg = iter(args)
return func(*(*(next(arg) if i is SKIP else i for i in positionals), *arg),
**{**keywords, **kwargs})
return wrapper
so that:
def f(x, y, z):
return x + 2 * y + 3 * z
print(partial_positionals(f, 4, SKIP, 6)(5))
outputs:
32

Scipy nquad integration with arguments

I tried to integrate in python using nquad. The problem is that when I try to pass extra arguments to the function which is being integrated in nquad, it wants to pass those parameters to bounds instead to function. I searched the internet and found that it was a bug in scipy.__version__ < 0.18.0, and was fixed then, but I have got version 1.1.0 and the problem persists. What should I do? The simplified example is below
>>> from scipy.integrate import nquad
>>> ranges0 = lambda x: [x, 2 * x]
>>> ranges = [ranges0, [1, 2]]
>>> func = lambda x0, x1, t0, t1: x0 + x1 + t0 + t1
>>> nquad(func, ranges, args=(1,2))
>>> TypeError: <lambda>() takes exactly 1 argument (3 given)
I did some digging in the documentation for nquad and I have found this excerpt:
If an element of ranges is a callable, then it will be called with all of the integration arguments available, as well as any parametric arguments. e.g. if func = f(x0, x1, x2, t0, t1), then ranges[0] may be defined as either (a, b) or else as (a, b) = range0(x1, x2, t0, t1).
In other words, when defining a func with 4 parameters you must define your range to take exactly 4-pos parameters. In other words, since your ranges0 is in the first place in the range list, it will be passed 4-1=3 parameters, if you put it in the next place in the list it will be passed 4-2=2 parameters. The same is true for all further places in the array. It stays at 2 because it is called with:
all of the integration arguments available
Here the problem is not related to scipy but it more relating to a logical error on your part.
In summary, a function in the range list can never accept one and only one argument when there are 2 time variables.

functools.partial: TypeError: got multiple values for keyword argument

I am using the partial method from the functools module to map a function over a range of values:
def basic_rule(p,b,vx=1,**kwargs):
return (p / b) if vx != 0 else 0
def rule5(func,**kwargs):
vals = map(functools.partial(func,**kwargs), range(1,kwargs['b']+1))
return [x for i,x in enumerate(vals[:-1]) if x >= vals[i+1]] == []
rule5(basic_rule,p=100,b=10000)
Here is the error I get on line 5:
----> return map(functools.partial(func,**kwargs), range(1,kwargs['b']+1))
TypeError: basic_rule() got multiple values for keyword argument 'p'
It looks like functools.partial is trying to assign the range to the argument p, even though I have already assigned a value to it. I'm trying to assign the range to the value of vx. Any idea how I can make that happen?
EDIT: Added a little bit of extra context to the code. Essentially what I'd like test 5 to do is ensure that the result of the function given to it increases as vt goes up, so that `func(vt=1) < func(vt=2)... < func(vt=n).
functools.partial generates a partial that stores the arguments receiveids in 2 properties:
arguments stores positional arguments
keywords stores all keyword-based arguments
So the partial can call original function exactly as was intended. In other words, when you call the resulting partial with one argument (let's say, 1) it would be the same as:
original_func(1, **kwargs)
As your kwargs contains the first argument - and you're passing the "1" as a positional argument - you get the error.
I'm not sure if it's gonna work in this particular case, but one solution could be use inspect.getargspec to extract arguments from kwargs that can be passed as positional arguments. In this case, the rule5 function would be similar to:
def rule5(func, **kwargs):
# let's save 'b' argument because we'll need it in the range call
b = kwargs['b']
original_args = inspect.getargspec(func).args
# extract from kwargs all arguments that can be passed as positional
new_args = [kwargs.pop(key) for key in original_args if key in kwargs]
# construct the partial passing as much positional arguments as possible
fn = functools.partial(func, *new_args, **kwargs)
# now map will pass the range result as the next positional argument
vals = map(fn, range(1, b+1))
return [x for i,x in enumerate(vals[:-1]) if x >= vals[i+1]] == []

Functions as arguments to functions

I saw this example in a Python book, which showcases how to use a function as an argument to another function:
def diff2(f, x, h=1E-6):
r = (f(x-h) - 2*f(x) + f(x+h))/float(h*h)
return r
def g(t):
return t**(-6)
t = 1.2
d2g = diff2(g, t)
print d2g
My question is, how does this script work without providing an argument to function g? The line in question is:
d2g = diff2(g,t)
Shouldn't it be done like:
d2g = diff2(g(t), t)
g is passed as an argument to diff2. In diff2, that argument is called f, so inside diff2 the name f refers to the function g. When diff2 calls f(x-h) (and the other calls it does), it is calling g, and providing the argument.
In other words, when you do diff2(g, t), you are telling diff2 that g is the function to call. The arguments to g are provided in diff2:
f(x-h) # calls g with x-h as the argument
f(x) # calls g with x as the argument
f(x+h) # calls g with x+h as the argument
If you called diff2(g(t), t), you would be passing the result of g(1.2) as the argument. g would be called before calling diff2, and diff2 would then fail when it tries to call f, because f would be a number (the value g(1.2)) instead of a function.
The functions in question are rather random, and perhaps difficult to understand. Let's consider a simple example, a function sum which takes two numbers a and b, and returns their sum. Actually, we can easily define another function prod, which returns their product too.
def sum(a,b):
return a + b
def prod(a,b):
return a * b
Let's say we have another function compute, which takes as its arguments the operation (a function), and two operands (two numbers to call the function on). In compute, we call the given operation on the arguments.
def compute(fn, a, b):
return fn(a, b)
We can compute different things. We can compute the sum of two numbers:
compute(sum, 1, 3)
We can compute the product of two numbers:
compute(prod, 1, 3)
Basically, without parentheses after the function name, we're not actually calling the function, it's just another object in the namespace (which happens to be a function which we can call). We don't call the function until inside of compute, when we do fn(a,b).
Let's see what the console outputs look like:
>>> compute(sum,1,3)
4
>>> compute(prod,1,3)
3
>>> sum
<function sum at mem-address>
>>> prod
<function prod at mem-address>
>>> sum(1,2)
3

Categories

Resources