view src/DLD-FUNCTIONS/odessa.cc @ 3984:addebffd4961

[project @ 2002-07-11 03:39:33 by jwe]
author jwe
date Thu, 11 Jul 2002 03:39:34 +0000
parents
children d4091aff6468
line wrap: on
line source

/*

Copyright (C) 2002 John W. Eaton

This file is part of Octave.

Octave is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.

Octave is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string>

#include <iomanip>
#include <iostream>

#include "ODESSA.h"
#include "lo-mappers.h"

#include "defun-dld.h"
#include "error.h"
#include "gripes.h"
#include "oct-obj.h"
#include "ov-fcn.h"
#include "pager.h"
#include "pr-output.h"
#include "unwind-prot.h"
#include "utils.h"
#include "variables.h"
#include "parse.h"

// Global pointer for user defined function required by odessa.
static octave_function *odessa_f;
static octave_function *odessa_j;
static octave_function *odessa_b;

static ODESSA_options odessa_opts;

// Is this a recursive call?
static int call_depth = 0;

static ColumnVector
odessa_user_f (double t, const ColumnVector& x, const ColumnVector& theta)
{
  ColumnVector retval;

  octave_value_list args;

  int n = x.length ();
  int npar = theta.length ();

  if (npar > 1)
    args(2) = theta;
  else if (npar == 1)
    args(2) = theta(0);
  else
    args(2) = Matrix ();

  if (n > 1)
    args(1) = x;
  else if (n == 1)
    args(1) = x(0);
  else
    args(1) = Matrix ();

  args(0) = t;

  if (odessa_f)
    {
      octave_value_list tmp = odessa_f->do_multi_index_op (1, args);

      if (error_state)
	{
	  gripe_user_supplied_eval ("odessa");
	  return retval;
	}

      if (tmp.length () > 0 && tmp(0).is_defined ())
	{
	  retval = ColumnVector (tmp(0).vector_value ());

	  if (error_state || retval.length () == 0)
	    gripe_user_supplied_eval ("odessa");
	}
      else
	gripe_user_supplied_eval ("odessa");
    }

  return retval;
}

static Matrix
odessa_user_mf (double t, const ColumnVector& x, const ColumnVector& theta,
		octave_function *mf)
{
  Matrix retval;

  if (mf)
    {
      octave_value_list args;

      int n = x.length ();
      int npar = theta.length ();

      if (npar > 1)
	args(2) = theta;
      else if (npar == 1)
	args(2) = theta(0);
      else
	args(2) = Matrix ();

      if (n > 1)
	args(1) = x;
      else if (n == 1)
	args(1) = x(0);
      else
	args(1) = Matrix ();

      args(0) = t;

      octave_value_list tmp = mf->do_multi_index_op (1, args);

      if (error_state)
	{
	  gripe_user_supplied_eval ("odessa");
	  return retval;
	}

      if (tmp.length () > 0 && tmp(0).is_defined ())
	{
	  retval = tmp(0).matrix_value ();

	  if (error_state || retval.length () == 0)
	    gripe_user_supplied_eval ("odessa");
	}
      else
	gripe_user_supplied_eval ("odessa");
    }

  return retval;
}

static Matrix
odessa_user_j (double t, const ColumnVector& x, const ColumnVector& theta)
{
  return odessa_user_mf (t, x, theta, odessa_j);
}

static ColumnVector
odessa_user_b (double t, const ColumnVector& x,
	       const ColumnVector& theta, int column)
{
  ColumnVector retval;

  if (odessa_b)
    {
      octave_value_list args;

      int n = x.length ();
      int npar = theta.length ();

      args(3) = static_cast<double> (column);

      if (npar > 1)
	args(2) = theta;
      else if (npar == 1)
	args(2) = theta(0);
      else
	args(2) = Matrix ();

      if (n > 1)
	args(1) = x;
      else if (n == 1)
	args(1) = x(0);
      else
	args(1) = Matrix ();

      args(0) = t;

      octave_value_list tmp = odessa_b->do_multi_index_op (1, args);

      if (error_state)
	{
	  gripe_user_supplied_eval ("odessa");
	  return retval;
	}

      if (tmp.length () > 0 && tmp(0).is_defined ())
	{
	  retval = ColumnVector (tmp(0).vector_value ());

	  if (error_state || retval.length () == 0)
	    gripe_user_supplied_eval ("odessa");
	}
      else
	gripe_user_supplied_eval ("odessa");
    }

  return retval;
}

static octave_value_list
make_list (const Array<Matrix>& m_array)
{
  octave_value_list retval;

  int len = m_array.length ();

  retval.resize (len);

  for (int i = 0; i < len; i++)
    retval(i) = m_array(i);

  return retval;
}

#define ODESSA_ABORT() \
  do \
    { \
      unwind_protect::run_frame ("Fodessa"); \
      return retval; \
    } \
  while (0)
 
#define ODESSA_ABORT1(msg) \
  do \
    { \
      ::error ("odessa: " msg); \
      ODESSA_ABORT (); \
    } \
  while (0)

#define ODESSA_ABORT2(fmt, arg) \
  do \
    { \
      ::error ("odessa: " fmt, arg); \
      ODESSA_ABORT (); \
    } \
  while (0)

// --------------------------------
// Everthing is so great above here
// --------------------------------

DEFUN_DLD (odessa, args, ,
  "odessa (\"f\", x_0, theta, sx_0, t_out, t_crit)\n\
\n\
The string \"f\" may be substituted for the vector of strings\n\
\n\
               [\"f\"; \"j\"; \"b\"] \n\
\n\
You can use the function @code{odessa_options} to set optional\n\
parameters for @code{odessa}.")
{
  octave_value_list retval;

  unwind_protect::begin_frame ("Fodessa");

  unwind_protect_int (call_depth);
  call_depth++;

  if (call_depth > 1)
    ODESSA_ABORT1 ("invalid recursive call");

  int nargin = args.length ();

  if (nargin < 5 || nargin > 6)
    {
      print_usage ("odessa");
      unwind_protect::run_frame ("Fodessa");
      return retval;
    }

  odessa_f = 0;
  odessa_j = 0;
  odessa_b = 0;

  octave_value f_arg = args(0);

  if (f_arg.is_string ())
    {
      string_vector f_str_arg = f_arg.all_strings ();
      
      int len = f_str_arg.length ();
      
      if (len > 2)
	{
	  string t = f_str_arg(2);

	  if (t.length () > 0)
	    {
	      odessa_b = is_valid_function (t, "odessa", true);
	      
	      if (! odessa_b)
		ODESSA_ABORT1
		  ("expecting b function name as argument 1");
	    }
	}

      if (len > 1)
	{
	  string t = f_str_arg(1);
	  
	  if (t.length () > 0)
	    {
	      odessa_j = is_valid_function (t, "odessa", true);
	      
	      if (! odessa_j)
		ODESSA_ABORT1
		  ("expecting function name as argument 1");
	    }
	}
      
      if (len > 0)
	odessa_f = is_valid_function (f_str_arg(0), "odessa", true);
      
      if (! odessa_f)
	ODESSA_ABORT1 ("expecting function name as argument 1");
    }
  
  ColumnVector state (args(1).vector_value ());

  if (error_state)
    ODESSA_ABORT1 ("expecting state vector as argument 2");

  bool have_parameters = false;
  
  ColumnVector theta;
  Matrix sensitivity_guess;

  if (nargin == 5 || nargin == 6)
    {
      octave_value theta_arg = args(2);

      if (! theta_arg.is_empty ())
	{
	  theta = ColumnVector (theta_arg.vector_value ());
	  
	  if (error_state)
	    ODESSA_ABORT1
	      ("expecting parameter vector as argument 3");
	}

      have_parameters = (theta.length () > 0);
      
      if (have_parameters)
	{
	  sensitivity_guess = args(3).matrix_value ();
	  
	  if (error_state)
	    ODESSA_ABORT1
	      ("expecting sensitivity guess as argument 4");
	  
	  if (sensitivity_guess.rows () != state.length ()
	      || sensitivity_guess.columns () != theta.length ())
	    ODESSA_ABORT1
	      ("incorrect dimension for sensitivity guess");
	}
    }

      ColumnVector out_times (args(4).vector_value ());

      if (error_state)
	ODESSA_ABORT1
	  ("expecting output time vector as %s argument 5");

      ColumnVector crit_times;

      bool crit_times_set = false;

      if (nargin==6)
	{
	  crit_times = ColumnVector (args(5).vector_value ());

	  if (error_state)
	    ODESSA_ABORT1
	      ("expecting critical time vector as argument 6");

	  crit_times_set = true;
	}

      ODESFunc func (odessa_user_f);

      if (odessa_j)
	func.set_jsub_function (odessa_user_j);

      if (odessa_b)
	func.set_bsub_function (odessa_user_b);

      double tzero = out_times (0);

      ODESSA_result output;

      if (have_parameters)
	{
	  ODESSA dae = ODESSA (state, theta, sensitivity_guess, tzero, func);
	  
	  dae.copy (odessa_opts);

	  if (crit_times_set)
	    output = dae.integrate (out_times, crit_times);
	  else
	    output = dae.integrate (out_times);
	}
      else
	{
	  ODESSA dae = ODESSA (state, tzero, func);
	  
	  dae.copy (odessa_opts);
	  
	  if (crit_times_set)
	    output = dae.integrate (out_times, crit_times);
	  else
	    output = dae.integrate (out_times);
	}

      if (! error_state)
	{
	  if (have_parameters)
	    {
	      retval(1) = make_list (output.state_sensitivity ());
	    }
	  
	  retval(0) = output.state ();
	}

  unwind_protect::run_frame ("Fodessa");

  return retval;
}
// -----------------------------
// EVERYTHING SWELL BELOW HERE
// -----------------------------


typedef void (ODESSA_options::*da_set_opt_mf) (const Array<double>&);
typedef void (ODESSA_options::*d_set_opt_mf) (double);
typedef void (ODESSA_options::*i_set_opt_mf) (int);
typedef void (ODESSA_options::*s_set_opt_mf) (const std::string&);

typedef Array<double> (ODESSA_options::*da_get_opt_mf) (void) const;
typedef double (ODESSA_options::*d_get_opt_mf) (void) const;
typedef int (ODESSA_options::*i_get_opt_mf) (void) const;
typedef std::string (ODESSA_options::*s_get_opt_mf) (void) const;

#define MAX_TOKENS 3

struct ODESSA_OPTIONS
{
  const char *keyword;
  const char *kw_tok[MAX_TOKENS + 1];
  int min_len[MAX_TOKENS + 1];
  int min_toks_to_match;
  da_set_opt_mf da_set_fcn;
  d_set_opt_mf d_set_fcn;
  i_set_opt_mf i_set_fcn;
  s_set_opt_mf s_set_fcn;
  da_get_opt_mf da_get_fcn;
  d_get_opt_mf d_get_fcn;
  i_get_opt_mf i_get_fcn;
  s_get_opt_mf s_get_fcn;
};

static ODESSA_OPTIONS odessa_option_table [] =
{
  { "absolute tolerance",
    { "absolute", "tolerance", 0, 0, },
    { 1, 0, 0, 0, }, 1,
    &ODESSA_options::set_absolute_tolerance, 0, 0, 0,
    &ODESSA_options::absolute_tolerance, 0, 0, 0, },

  { "initial step size",
    { "initial", "step", "size", 0, },
    { 3, 0, 0, 0, }, 1,
    0, &ODESSA_options::set_initial_step_size, 0, 0,
    0, &ODESSA_options::initial_step_size, 0, 0, },

  { "integration method",
    { "integration", "method", 0, 0, },
    { 3, 0, 0, 0, }, 1,
    0, 0, 0, &ODESSA_options::set_integration_method,
    0, 0, 0, &ODESSA_options::integration_method, },

  { "maximum step size",
    { "maximum", "step", "size", 0, },
    { 2, 0, 0, 0, }, 1,
    0, &ODESSA_options::set_maximum_step_size, 0, 0,
    0, &ODESSA_options::maximum_step_size, 0, 0, },

  { "minimum step size",
    { "minimum", "step", "size", 0, },
    { 2, 0, 0, 0, }, 1,
    0, &ODESSA_options::set_minimum_step_size, 0, 0,
    0, &ODESSA_options::minimum_step_size, 0, 0, },

  { "relative tolerance",
    { "relative", "tolerance", 0, 0, },
    { 1, 0, 0, 0, }, 1,
    0, &ODESSA_options::set_relative_tolerance, 0, 0,
    0, &ODESSA_options::relative_tolerance, 0, 0, },

  { "step limit",
    { "step", "limit", 0, 0, },
    { 1, 0, 0, 0, }, 1,
    0, 0, &ODESSA_options::set_step_limit, 0,
    0, 0, &ODESSA_options::step_limit, 0, },

  { 0,
    { 0, 0, 0, 0, },
    { 0, 0, 0, 0, }, 0,
    0, 0, 0, 0,
    0, 0, 0, 0, },
};

static void
print_odessa_option_list (std::ostream& os)
{
  print_usage ("odessa_options", 1);

  os << "\n"
     << "Options for odessa include:\n\n"
     << "  keyword                                  value\n"
     << "  -------                                  -----\n\n";

  ODESSA_OPTIONS *list = odessa_option_table;

  const char *keyword;
  while ((keyword = list->keyword) != 0)
    {
      os << "  "
	 << std::setiosflags (std::ios::left) << std::setw (40)
	 << keyword
	 << std::resetiosflags (std::ios::left)
	 << " ";

      if (list->da_get_fcn)
	{
	  Array<double> val = (odessa_opts.*list->da_get_fcn) ();
	  if (val.length () == 1)
	    {
	      if (val(0) < 0.0)
		os << "computed automatically";
	      else
		os << val(0);
	    }
	  else
	    {
	      os << "\n\n";
	      // XXX FIXME XXX
	      Matrix tmp = Matrix (ColumnVector (val));
	      octave_print_internal (os, tmp, false, 2);
	      os << "\n";
	    }
	}
      else if (list->d_get_fcn)
	{
	  double val = (odessa_opts.*list->d_get_fcn) ();
	  if (val < 0.0)
	    os << "computed automatically";
	  else
	    os << val;
	}
      else if (list->i_get_fcn)
	{
	  int val = (odessa_opts.*list->i_get_fcn) ();
	  if (val < 0)
	    os << "infinite";
	  else
	    os << val;
	}
      else if (list->s_get_fcn)
	{
	  os << (odessa_opts.*list->s_get_fcn) ();
	}
      else
	panic_impossible ();

      os << "\n";
      list++;
    }

  os << "\n";
}

static void
set_odessa_option (const std::string& keyword, double val)
{
  ODESSA_OPTIONS *list = odessa_option_table;

  while (list->keyword != 0)
    {
      if (keyword_almost_match (list->kw_tok, list->min_len, keyword,
				list->min_toks_to_match, MAX_TOKENS))
	{
	  if (list->da_set_fcn)
	    {
	      Array<double> tmp (1, val);
	      (odessa_opts.*list->da_set_fcn) (tmp);
	    }
	  else if (list->d_set_fcn)
	    {
	      (odessa_opts.*list->d_set_fcn) (val);
	    }
	  else
	    {
	      if (xisnan (val))
		{
		  error ("odessa_options: %s: expecting integer, found NaN",
			 keyword.c_str ());
		}
	      else
		(odessa_opts.*list->i_set_fcn) (NINT (val));
	    }
	  return;
	}
      list++;
    }

  warning ("odessa_options: no match for `%s'", keyword.c_str ());
}

static void
set_odessa_option (const std::string& keyword, const Array<double>& val)
{
  ODESSA_OPTIONS *list = odessa_option_table;

  while (list->keyword != 0)
    {
      if (keyword_almost_match (list->kw_tok, list->min_len, keyword,
				list->min_toks_to_match, MAX_TOKENS))
	{
	  if (list->da_set_fcn)
	    (odessa_opts.*list->da_set_fcn) (val);
	  else
	    error ("odessa_options: no function to handle vector option");

	  return;
	}
      list++;
    }

  warning ("odessa_options: no match for `%s'", keyword.c_str ());
}

static void
set_odessa_option (const std::string& keyword, const std::string& val)
{
  ODESSA_OPTIONS *list = odessa_option_table;

  while (list->keyword != 0)
    {
      if (keyword_almost_match (list->kw_tok, list->min_len, keyword,
				list->min_toks_to_match, MAX_TOKENS))
	{
	  if (list->s_set_fcn)
	    (odessa_opts.*list->s_set_fcn) (val);
	  else
	    error ("odessa_options: no function to handle string option");

	  return;
	}
      list++;
    }

  warning ("odessa_options: no match for `%s'", keyword.c_str ());
}

static octave_value_list
show_odessa_option (const std::string& keyword)
{
  octave_value retval;

  ODESSA_OPTIONS *list = odessa_option_table;

  while (list->keyword != 0)
    {
      if (keyword_almost_match (list->kw_tok, list->min_len, keyword,
				list->min_toks_to_match, MAX_TOKENS))
	{
	  if (list->da_get_fcn)
	    {
	      Array<double> val = (odessa_opts.*list->da_get_fcn) ();
	      if (val.length () == 1)
		{
		  if (val(0) < 0.0)
		    retval = "computed automatically";
		  else
		    retval = val(0);
		}
	      else
		retval = ColumnVector (val);
	    }
	  else if (list->d_get_fcn)
	    {
	      double val = (odessa_opts.*list->d_get_fcn) ();
	      if (val < 0.0)
		retval = "computed automatically";
	      else
		retval = val;
	    }
	  else if (list->i_get_fcn)
	    {
	      int val = (odessa_opts.*list->i_get_fcn) ();
	      if (val < 0)
		retval = "infinite";
	      else
		retval = static_cast<double> (val);
	    }
	  else if (list->s_get_fcn)
	    {
	      retval = (odessa_opts.*list->s_get_fcn) ();
	    }
	  else
	    panic_impossible ();

	  return retval;
	}
      list++;
    }

  warning ("odessa_options: no match for `%s'", keyword.c_str ());

  return retval;
}

DEFUN_DLD (odessa_options, args, ,
  "-*- texinfo -*-\n\
@deftypefn {Loadable Function} {} odessa_options (@var{opt}, @var{val})\n\
When called with two arguments, this function allows you set options\n\
parameters for the function @code{odessa}.  Given one argument,\n\
@code{odessa_options} returns the value of the corresponding option.  If\n\
no arguments are supplied, the names of all the available options and\n\
their current values are displayed.\n\
@end deftypefn")
{
  octave_value_list retval;

  int nargin = args.length ();

  if (nargin == 0)
    {
      print_odessa_option_list (octave_stdout);
    }
  else if (nargin == 1 || nargin == 2)
    {
      std::string keyword = args(0).string_value ();

      if (! error_state)
	{
	  if (nargin == 1)
	    retval = show_odessa_option (keyword);
	  else
	    {
	      if (args(1).is_string ())
		{
		  std::string val = args(1).string_value ();

		  if (! error_state)
		    set_odessa_option (keyword, val);
		}
	      else if (args(1).is_scalar_type ())
		{
		  double val = args(1).double_value ();

		  if (! error_state)
		    set_odessa_option (keyword, val);
		}
	      else
		{
		  Array<double> val = args(1).vector_value ();

		  if (! error_state)
		    set_odessa_option (keyword, val);
		}
	    }
	}
    }
  else
    print_usage ("odessa_options");

  return retval;
}

/*
;;; Local Variables: ***
;;; mode: C++ ***
;;; End: ***
*/