Is there a way to return tuple of mixed variables in Jax helper function? - jax

On my path learning Jax, I tried to achieve something like
def f(x):
return [x + 1, [1,2,3], "Hello"]
x = 1
new_x, a_list, str = jnp.where(
x > 0,
test(x),
test(x + 1)
)
Well, Jax clearly does not support this. I tried searching online and went through quite a few docs, but I couldn't find a good answer.
Any help on how can I achieve this in Jax?

In general, JAX functions like jnp.where only accept array arguments, not list or string arguments. Since you're using a function that is not compatible with JAX in the first place, it might be better to just avoid JAX conditionals and just use standard Python conditionals instead:
import jax.numpy as jnp
def f(x):
return [x + 1, [1,2,3], "Hello"]
x = 1
new_x, a_list, str_ = f(x) if x > 0 else f(x + 1)

Related

Can't convert an iterator of tuple to an iterator of strings

I'm trying to convert an iterator of tuples to an iterator of strings.
I must use itertools, so I'm not allowed to use either for or while.
import itertools
def parlar_lloro(it0):
it1 = filter(lambda x: len(x)>=5, it0)
it2 = map(lambda x: x[:len(x)-2], it1)
it3, it4 = itertools.tee(it2, 2)
n = len(list(it3))
itn = itertools.repeat('CROA', n)
ite = zip(it4, itn)
return itr
What I get when executing this on Python's Shell is:
>>> [(abc,'CROA'),(def,'CROA'),(ghi,'CROA')]
And I want:
>>> ['abc','CROA','def','CROA','ghi','CROA']
If you're suppose to be using itertools then I suspect what you're expected to use is itertools.chain...
Change your return to be (I'm guessing itr is a typo and you meant ite):
return itertools.chain.from_iterable(ite)
Just for reference, the same thing can be accomplished using a list-comp:
res = [sub for el in ((it[:-2], 'CROA') for it in x if len(it) >= 5) for sub in el]

How does one get a parameter from a params (pytree) in haiku? (jax framework)

For example you set up a module and that has params. But if you want do regularize something in a loss what is the pattern?
import jax.numpy as jnp
import jax
def loss(params, x, y):
l = jnp.sum((y - mlp.apply(params, x)) ** 2)
w = hk.get_params(params, 'w') # does not work like this
l += jnp.sum(w ** w)
return l
There is some pattern missing in the examples.
params is essentially a read-only dictionary, so you can get the value of a parameter by treating it as a dictionary:
print(params['w'])
If you want to update the parameters, you cannot do it in-place, but have to first convert it to a mutable dictionary:
params_mutable = hk.data_structures.to_mutable_dict(params)
params_mutable['w'] = 3.14
params_new = hk.data_structures.to_immutable_dict(params_mutable)

oneliner using reduce in python

I'm trying to take my Python skills (beginner) to the next level.
and I'm trying to teach myself the functools.reduce() function
So I'm running every time into the same error;
I'd appreciate if someone could explain to me what is the problem in my code
I'm trying to build a simple func that receive a number and return the sum of number digits
import functools
def sum_using_reduce(number):
return str(number)[0] + str(number)[1]
number = 104
print(functools.reduce(sum_using_reduce, number))
Try this:
number = 104
functools.reduce(lambda x, y: x+y, [int(i) for i in str(number)])
Output: 5
Using your example:
import functools
def sum_using_reduce(x, y) -> int:
return x + y
print(functools.reduce(sum_using_reduce, [int(i) for i in str(105)]))
Output: 6
Another approach:
import functools
def sum_using_reduce(number: int) -> int:
return functools.reduce(lambda x, y: x+y, [int(i) for i in str(number)])
print(sum_using_reduce(124))
Output: 7
In your sum_using_reduce function you are trying to sum two strings, which would simply perform concatenation. Moreover, you are providing an integer as the second argument to the reduce function, where the reduce function requires an iterable to be provided.
Below is a solution that fixes both these requirements:
from functools import reduce
number=104
print(reduce(lambda x,y:x+y,map(int,str(number))))
map(int,str(number)) transforms the number to a string (104->"104") and then turns every character in the string to an integer, returning an iterable map object ("104"->[1,0,4]).
lambda x,y:x+y is a function which takes two integers and sums them.

Python3, scipy.optimize: Fit model to multiple datas sets

I have a model which is defined as:
m(x,z) = C1*x^2*sin(z)+C2*x^3*cos(z)
I have multiple data sets for different z (z=1, z=2, z=3), in which they give me m(x,z) as a function of x.
The parameters C1 and C2 have to be the same for all z values.
So I have to fit my model to the three data sets simultaneously otherwise I will have different values of C1 and C2 for different values of z.
It this possible to do with scipy.optimize.
I can do it for just one value of z, but can't figure out how to do it for all z's.
For one z I just write this:
def my_function(x,C1,C1):
z=1
return C1*x**2*np.sin(z)+ C2*x**3*np.cos(z)
data = 'some/path/for/data/z=1'
x= data[:,0]
y= data[:,1]
from lmfit import Model
gmodel = Model(my_function)
result = gmodel.fit(y, x=x, C1=1.1)
print(result.fit_report())
How can I do it for multiple set of datas (i.e different z values?)
So what you want to do is fit a multi-dimensional fit (2-D in your case) to your data; that way for the entire data set you get a single set of C parameters that bests describes your data. I think the best way to do this is using scipy.optimize.curve_fit().
So your code would look something like this:
import scipy.optimize as optimize
import numpy as np
def my_function(xz, *par):
""" Here xz is a 2D array, so in the form [x, z] using your variables, and *par is an array of arguments (C1, C2) in your case """
x = xz[:,0]
z = xz[:,1]
return par[0] * x**2 * np.sin(z) + par[1] * x**3 * np.cos(z)
# generate fake data. You will presumable have this already
x = np.linspace(0, 10, 100)
z = np.linspace(0, 3, 100)
xx, zz = np.meshgrid(x, z)
xz = np.array([xx.flatten(), zz.flatten()]).T
fakeDataCoefficients = [4, 6.5]
fakeData = my_function(xz, *fakeDataCoefficients) + np.random.uniform(-0.5, 0.5, xx.size)
# Fit the fake data and return the set of coefficients that jointly fit the x and z
# points (and will hopefully be the same as the fakeDataCoefficients
popt, _ = optimize.curve_fit(my_function, xz, fakeData, p0=fakeDataCoefficients)
# Print the results
print(popt)
When I do this fit I get precisely the fakeDataCoefficients I used to generate the function, so the fit works well.
So the conclusion is that you don't do 3 fits independently, setting the value of z each time, but instead you do a 2D fit which takes the values of x and z simultaneously to find the best coefficients.
Your code is incomplete and has a few syntax errors.
But I think that you want to build a model that concatenates the models for the different data sets, and then fit the concatenated data to that model. Within the context of lmfit (disclosure: author and maintainer), I often find it easier to use minimize() and an objective function for multiple data set fits rather than the Model class. Perhaps start with something like this:
import lmfit
import numpy as np
# define the model function for each dataset
def my_function(x, c1, c2, z=1):
return C1*x**2*np.sin(z)+ C2*x**3*np.cos(z)
# Then write an objective function like this
def f2min(params, x, data2d, zlist):
ndata, npts = data2d.shape
residual = 0.0*data2d[:]
for i in range(ndata):
c1 = params['c1_%d' % (i+1)].value
c2 = params['c2_%d' % (i+1)].value
residual[i,:] = data[i,:] - my_function(x, c1, c2, z=zlist[i])
return residual.flatten()
# now build that `data2d`, `zlist` and build the `Parameters`
data2d = []
zlist = []
x = None
for fname in dataset_names:
d = np.loadtxt(fname) # or however you read / generate data
if x is None: x = d[:, 0]
data2d.append(d[:, 1])
zlist.append(z_for_dataset(fname)) # or however ...
data2d = np.array(data2d) # turn list into nd array
ndata, npts = data2d.shape
params = lmfit.Parameters()
for i in range(ndata):
params.add('c1_%d' % (i+1), value=1.0) # give a better starting value!
params.add('c2_%d' % (i+1), value=1.0) # give a better starting value!
# now you're ready to do the fit and print out the results:
result = lmfit.minimize(f2min, params, args=(x, data2d, zlist))
print(results.fit_report())
That code really a sketch and is all untested, but hopefully will give you a good starting foundation.

Numpy: division by zero error, but mathematically function is apparently defined

I'm testing out some functions to fit with data, and one of them (in 2-D) is
f(x) = (1/(1-x)) / (1 + 1/(1-x))
Which, according to Wolfram and the Google plotters, gives you the result
f(1) = 1
I've tried to get this to work without hard coding the case
if x == 1:
return 1
but I end up with a nan and a RunTimeWarning informing me that I have indeed divided by zero.
import numpy as np
def f(x):
return 1/(1-x) / (1 + 1/(1-x))
x_range = np.linspace(0, 1, 50)
y = f(x_range)
print(y)
Is there a more elegant solution than to simply introduce a hard-coded if?
Is there a reason to keep it in this form, you can simplify it to:
def f(x):
return 1/(2-x)
Wolfram and Google probably to some sort of algebraic simplification too.
Just simplify the equation for f(x) = (1/(1-x)) / (1 + 1/(1-x)). The simplified equation will be (1/(2-x)). Now update the program as:
import numpy as np
def f(x):
return 1/(2-x)
x_range = np.linspace(0, 1, 50)
y = f(x_range)
print(y)
output:
[0.5 0.50515464 0.51041667 0.51578947 0.5212766 0.52688172
0.5326087 0.53846154 0.54444444 0.5505618 0.55681818 0.56321839
0.56976744 0.57647059 0.58333333 0.59036145 0.59756098 0.60493827
0.6125 0.62025316 0.62820513 0.63636364 0.64473684 0.65333333
0.66216216 0.67123288 0.68055556 0.69014085 0.7 0.71014493
0.72058824 0.73134328 0.74242424 0.75384615 0.765625 0.77777778
0.79032258 0.80327869 0.81666667 0.83050847 0.84482759 0.85964912
0.875 0.89090909 0.90740741 0.9245283 0.94230769 0.96078431
0.98 1. ]

Resources