0

Can anyone tell me what's wrong in my code below ?

I am a casual user of pymc2, generally for solving physical equations. I have troubles to adapt a fit to pymc3 and the documentation seems to me unclear. Also I did not recognize my problem on forums, probably because I don’t know what is my problem…

I use the find_MAP method to get a first guess of fitted values but this first guess is completly wrong (not even inside the physical limits) and a warning tells me there are discrete variables (which is wrong), implying the gradient is not available.

The aim is to fit some parameters in a diffusion equation: here alpha0, alpha1 and epsilon, that are continuous and a priori uniformly distributed. During a long debugging time I anti-optimized the code, so I don’t think the code is interesting by itself. Just know that the pymc2 version is ok and works fine. As I don’t know where the problem(s) is(are), I also give the inside of the ‘simul_DifferentialEq’ function, but the pymc3 stuff is below the corresponding comment.

import numpy as np
from scipy.interpolate import interp1d
import pymc3 as pm3
import theano.tensor as tt
import theano.compile
import config
@theano.compile.ops.as_op(itypes=[tt.dscalar,tt.dscalar,tt.dscalar],
                          otypes=[tt.dvector])
def simul_DifferentialEq(alpha0,alpha1,epsilon):
    observed_depth = np.array([0.5,1.5,3,5,7,9,13,17])# in cm
    observed_values = np.array([6.25,2.75,1.25,1.25,1.5,1.75,1.5,1])# mmol.l-1
    #useful ?
    observed_values = observed_values[np.argsort(-observed_depth)]
    observed_depth = -np.sort(-observed_depth)
    depth = config.depth
    matA = config.matA
    matB = config.matB
    matC = config.matC
    concentration = config.concentration
    alpha = alpha0 * np.exp(-alpha1*depth) # in day^-1
    # simplification for a Constant phi
    phi = np.empty(len(depth))
    phi[:]=0.6
    #########################
    beta = config.beta * phi * epsilon # dimensionless
    delta = beta * phi / config.Deltax
    eta = alpha * config.Deltat
    f1 = f2 = delta
    f3 = 1 - 2*delta + eta/2
    matB[0,0] = f1[0] - eta[0]/2 +1
    matB[0,1] = matA[0,1] = -2*f1[0]
    matB[0,2] = matA[0,2] = delta[0]
    matB[-1,-1] = f2[-1] + eta[-1]/2 +1
    matB[-1,-2] = matA[-1,-2] = -2*f2[-1]
    matB[-1,-3] = matA[-1,-3] = delta[-1]
    matB[range(1,concentration.sizex-1),
         range(1,concentration.sizex-1)] = \
         f3[1:concentration.sizex-1]
    matA[range(1,concentration.sizex-1),
         range(concentration.sizex-2)] = \
         matB[range(1,concentration.sizex-1),
         range(concentration.sizex-2)] = \
         f1[1:concentration.sizex-1] 
    matA[range(1,concentration.sizex-1),
         range(2,concentration.sizex)] = \
         matB[range(1,concentration.sizex-1),
         range(2,concentration.sizex)] = \
         f2[1:concentration.sizex-1]
    matB[range(1,concentration.sizex),0] = -eta[1:]
    matA[range(concentration.sizex),
         range(concentration.sizex)] = \
         matB[range(concentration.sizex),
         range(concentration.sizex)] -2
    matA[0,0] += eta[0]
    matC = np.dot(np.linalg.matrix_power(-matA,-1),matB)
    for tcount in range(concentration.sizet-1):
        #the variable 'temp' has no interest (just convenient for debugging)
        temp = np.dot(matC,concentration.values[:,tcount])
        # condition limit
        temp[0] = config.C0
        # a priori useless (but convenient for debugging))
        temp[np.where(temp>config.C0)] = config.C0
        # everything for that...     
        concentration.values[:,tcount+1] = temp
    interpolated_concentration = interp1d(depth,concentration.values[:,-1])
    return interpolated_concentration(observed_depth)
# the pymc3 stuff is below
model = pm3.Model()
with model:
    alpha0 = pm3.Uniform("alpha0",-2,0)
    alpha1 = pm3.Uniform("alpha1",-1,2)
    epsilon = pm3.Uniform("epsilon",0.1,15)
    DifferentialEq = simul_DifferentialEq(alpha0,alpha1,epsilon)
    # it is awkward to repeat observed values
    #some previous tries made me think it could solve the problem but it didn't
    observed_depth = np.array([0.5,1.5,3,5,7,9,13,17])# in cm
    observed_values = np.array([6.25,2.75,1.25,1.25,1.5,1.75,1.5,1])# mmol.l-1
    # useful ?
    observed_values = observed_values[np.argsort(-observed_depth)]
    observed_depth = -np.sort(-observed_depth)
    obs = pm3.Normal('obs', mu=DifferentialEq, sd=0.1, observed=observed_values)
    print('running test170127, find_MAP...')
    testfindmap = pm3.find_MAP()

Thank you for your attention, the content of the config.py is:

C0=Cowl0 = 10 # in mmol/l: concentration at the surface (at t=0), sometimes noted C0
Dsw = 1.6 # in cm^2.d-1
Cdefault = 1e-10 # concentration at t=0, depth>0

# maximum depth and time in the simulation for solving the ED (assuming it begins at x=t=0)
maxdepth = 17 # in cm
maxtime = 1  # in day

#steps in depth and time in the simulation for solving the ED
Deltax = 0.05# in cm
Deltat = 0.02# in day

##############################################
# internal cooking

from numpy import arange, empty, zeros
from solve_ED_crank import sph_2Dfunct
depth = (arange(maxdepth/Deltax +1))*Deltax # in cm
time = (arange(maxtime/Deltat +1))*Deltat # in day
beta = Dsw * Deltat / (2 * Deltax)

matA = zeros([len(depth),len(depth)])
matB = zeros([len(depth),len(depth)])
matC = empty([len(depth),len(depth)])

concentration_t0 = empty(len(depth))
concentration_t0[1:] = Cdefault
concentration_t0[0] = Cowl0
concentration = sph_2Dfunct(sizex=len(depth),
                            sizet=len(time),
                            firstline=concentration_t0)

comment tuesday the 07/02 at ~12:30.

I replaced the last line (ie. the find_MAP stuff) with:

pm3.sample(500)

when I run the main code, I get:

Auto-assigning NUTS sampler...
INFO:pymc3:Auto-assigning NUTS sampler...
Initializing NUTS using advi...
INFO:pymc3:Initializing NUTS using advi...
Traceback (most recent call last):

  File "<ipython-input-1-8395e07601b2>", line 1, in <module>
    runfile('/Users/steph/work/profiles/profiles-pymc/test170127.py', wdir='/Users/steph/work/profiles/profiles-pymc')

  File "/Users/steph/anaconda/lib/python3.5/site-packages/spyder/utils/site/sitecustomize.py", line 866, in runfile
    execfile(filename, namespace)

  File "/Users/steph/anaconda/lib/python3.5/site-packages/spyder/utils/site/sitecustomize.py", line 102, in execfile
    exec(compile(f.read(), filename, 'exec'), namespace)

  File "/Users/steph/work/profiles/profiles-pymc/test170127.py", line 84, in <module>
    pm3.sample(500)

  File "/Users/steph/anaconda/lib/python3.5/site-packages/pymc3/sampling.py", line 149, in sample
    start_, step = init_nuts(init=init, n_init=n_init, model=model)

  File "/Users/steph/anaconda/lib/python3.5/site-packages/pymc3/sampling.py", line 434, in init_nuts
    v_params = pm.variational.advi(n=n_init)

  File "/Users/steph/anaconda/lib/python3.5/site-packages/pymc3/variational/advi.py", line 139, in advi
    updates = optimizer(loss=-1 * elbo, param=[uw_shared])

  File "/Users/steph/anaconda/lib/python3.5/site-packages/pymc3/variational/advi.py", line 259, in optimizer
    grad = tt.grad(loss, param_)

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 561, in grad
    grad_dict, wrt, cost_name)

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1324, in _populate_grad_dict
    rval = [access_grad_cache(elem) for elem in wrt]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1324, in <listcomp>
    rval = [access_grad_cache(elem) for elem in wrt]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in access_term_cache
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 973, in <listcomp>
    output_grads = [access_grad_cache(var) for var in node.outputs]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1279, in access_grad_cache
    term = access_term_cache(node)[idx]

  File "/Users/steph/anaconda/lib/python3.5/site-packages/theano/gradient.py", line 1113, in access_term_cache
    input_grads = node.op.grad(inputs, new_output_grads)

AttributeError: 'FromFunctionOp' object has no attribute 'grad'

I would add: if I keep the call to find_MAP, the code runs without any error but the resulting values look absurd and I get this double-warning:

Warning: gradient not available.(E.g. vars contains discrete variables). MAP estimates may not be accurate for the default parameters. Defaulting to non-gradient minimization fmin_powell.WARNING:pymc3:Warning: gradient not available.(E.g. vars contains discrete variables). MAP estimates may not be accurate for the default parameters. Defaulting to non-gradient minimization fmin_powell.
    Optimization terminated successfully.
             Current function value: 36.569283
             Iterations: 10
             Function evaluations: 415
Stéphane
  • 1,389
  • 3
  • 13
  • 34
  • could you replace the "config" stuff with actual values? ```find_MAP``` is just a wrapper around scipy minimizer, Do you get any error if you sample (using NUTS or Metropolis)? – aloctavodia Feb 01 '17 at 15:20
  • 1. NUTS & sample both give me 'AttributeError: 'FromFunctionOp' object has no attribute 'grad'' after a bunch of messages – Stéphane Feb 03 '17 at 13:02
  • Could you post a reproducible example? – aloctavodia Feb 06 '17 at 18:54
  • done... thank you again, I hope the error message is reproductible. – Stéphane Feb 07 '17 at 11:37
  • I found a pymc2 -> pymc3 example on http://stackoverflow.com/questions/24804298/fit-a-non-linear-function-to-data-observations-with-pymcmc-pymc. maybe this will answer my question – Stéphane Feb 08 '17 at 09:57

1 Answers1

2

As I eventually understand after hours (the pymc3 doc is definitely a pain !), deterministic functions that are given independently of pymc3 (like black boxes), through a 'thenano' decorator, have no defined gradient and thus cannot use any stuff demanding gradient. I don't know why it was not a problem in pymc2 or maybe it was implicit. Can anyone tell me ? My code works well with non-gradient method like:

step = pm3.Metropolis()
trace = pm3.sample(10000,step)

but what I still don't get is the output of find_MAP, that is different for Normal and Uniform variables: in the first case find_MAP seems to return the guess as a value ; in the second case find_MAP returns something (what is it ?!) with an 'interval' suffix added to the variable name.

Stéphane
  • 1,389
  • 3
  • 13
  • 34
  • Sorry for the late reply. AFAIK PyMC2 did not have gradient based methods, in fact implementing NUTS was one of the reason to adopt Theano and move from PyMC2 to PyMC3. I think you can use the decorator with gradient-based methods IF you provide the gradient, but I am not sure about this and I have never tried it my self (although I would like to explore this). I see your question about find_MAP has already been answered. About the documentation I think there is plenty of room for improvement. Do you want to open an issue about it or send a PR improving it? Contributions are welcome! – aloctavodia Feb 14 '17 at 01:19
  • thanks for your attention, my find_map question is indeed detailled and answered here http://stackoverflow.com/questions/42146962/what-does-the-find-map-output-mean-in-pymc3 and some other troubles to use pymc3 are described there http://stackoverflow.com/questions/42205123/how-to-fit-a-method-belonging-to-an-instance-with-pymc3 – Stéphane Apr 10 '17 at 06:43