lsquares



Hi,

I just found the lsquares package in maxima. I have couple of questions:

1) Is it possible to simultaneously fit multiple data sets with each 
having its own fitting function in such a way that they share parameters?
For example, I run into this sort of problem often in chemical kinetics 
where multiple components are followed experimentally and each have 
their own kinetic equation. The rate constants are shared between the 
two equations when fitting the data.

2) Are there routines to calculate r^2, covariance matrix, standard 
error estimates ? Of course, for non-linear least squares things are a 
bit tricky but many things can be done at least approximately.

I have been using the following maxima program for (non-linear) least 
squares. I would like to get rid of this and use the built-in routines 
in maxima.

Thanks,

Jussi Eloranta
Department of chemistry and biochemistry
Cal State Northridge

---

/*
 * Unconstrained nonlinear least squares fit.
 *
 * nlsq(funcs, ydata, xdata, yvars, xvars, params, initvars, errtol, debug)
 *
 * funcs    = list of functions for fitting [fun1, fun2, ...].
 * ydata    = list of y-data vectors [yvec1, yvec2, ...].
 * xdata    = list of x-data vectors [xvec1, xvec2, ...].
 * yvars    = list of y variables for the functions given in funcs [y1, 
y2, ...].
 * xvars    = list of x variables for the functions given in funcs [x1, 
x2, ...].
 * params   = list of parameters to be fitted [a, b, ...].
 * initvars = list of initial values for params [ai, bi, ...].
 * errtol   = requested error tolerance in BFGS (1E-4).
 * debug    = optimization output vector for BFGS ([1, 2] = full output, 
[-1 0] = no output).
 *
 * Returns: [F, R2, V, C, E].
 *
 * F  = list of optimized functions.
 * R2 = list of r^2 values for each function.
 * V  = list of optimized parameter values.
 * C  = covariance matrix for the parameters.
 * E  = list of standard errors for parameters.
 *
 */

load("lbfgs");

nlsq([ArgList]):=
  block([narg, funcs, ydata, xdata, yvars, xvars, params, initvars, errtol,
         debug, neqs, i, sq, tmp, lbfgs_nfeval_max:1000, /* BFGS tends 
to converge somewhat slowly... */
         fun, fit, F, ss, sstot, sm, st, R2, C, lp, E, sigma2, Vm, k, np],

  narg:length(ArgList),
  if narg # 9 then (
    print("nlsq: bad number of function arguments (requires 9 arguments)."),
    return(false)
  ),

  funcs:ArgList[1],
  ydata:ArgList[2],
  xdata:ArgList[3],
  yvars:ArgList[4],
  xvars:ArgList[5],
  params:ArgList[6],
  initvars:ArgList[7],
  errtol:ArgList[8],
  debug:ArgList[9],

  neqs:length(funcs),
  lp:length(params),

  if neqs # length(ydata) or neqs # length(xdata) or neqs # 
length(yvars) or neqs # length(xvars) then (
    print("nlsq: Inconsistent lengths for arguments 1 - 5."),
    return(false)
  ),

  /* Construct the least squares sum and call lbfgs to optimize */
  tmp:0,
  for i:1 thru neqs do (
    if length(xdata[i]) # length(ydata[i]) then (
      print("nlsq: Inconsistent lengths for X and Y in set ", i),
      return(false)
    ),
    sq:(lhs(funcs[i]) - rhs(funcs[i]))^2,
    sq:subst('xdata[i][j], xvars[i], sq),
    sq:subst('ydata[i][j], yvars[i], sq),
    tmp:tmp + 'sum(sq, j, 1, length(xdata[i]))
  ),
/*  fun:ev(tmp,nouns),*/
  fun:tmp,
  fit:lbfgs(fun, params, initvars, errtol, debug),
  if fit = [] then (
    print("nlsq: BFGS convergence error. Fit failed."),
    return(false)
  ),

  /* Substitute the optimized values back into the equations */
  F:makelist(0,i,1,neqs),
  for i:1 thru neqs do (
    F[i]:float(rhs(funcs[i])),
    for j:1 thru lp do F[i]:subst(rhs(fit[j]), params[j], F[i])
  ),

  /* Calculate r^2 for each data set */
  sstot:0,
  R2:makelist(0,i,1,neqs),
  for i:1 thru neqs do (
    ss:sum((subst(xdata[i][j], xvars[i], F[i]) - ydata[i][j])^2, j, 1, 
length(xdata[i])),
    sstot:sstot + ss,
    sm:sum(ydata[i][j], j, 1, length(xdata[i])) / length(xdata[i]),
    st:sum((subst(xdata[i][j], xvars[i], F[i]) - sm)^2, j, 1, 
length(xdata[i])),
    R2[i]:float(ev(1 - ss / st,nouns))
  ),

  /* Calculate the covariance matrix */
  C:ematrix(lp, lp, 0, 1, 1),
  for i:1 thru lp do
    for j:1 thru lp do (
      C[i,j]:diff(fun,params[i],1,params[j],1),
      for k:1 thru lp do C[i,j]:subst(rhs(fit[k]),params[k],C[i,j])
    ),
  C:float(ev(C,nouns)),
  /* watch out when lp = 1, invert returns a number not a matrix */
  if lp = 1 then (
    C[1,1]:1/C[1,1]
  ) else (
    C:invert(C)
  ),

  /* Calculate standard errors */
  np:sum(length(xdata[i]), i, 1, neqs),
  sigma2:sstot / (np - lp),
  E:makelist(0,i,1,lp),
  V:makelist(0,i,1,lp),
  for i:1 thru lp do (
    E[i]:float(sqrt(sigma2 * C[i,i])),
    V[i]:float(rhs(fit[i]))
  ),

  return([F, R2, V, C, E])
);