changeset 6691:1a404c6aba1c octave-forge

control-oo: solve AREs with SLICOT
author paramaniac
date Sat, 13 Feb 2010 13:06:39 +0000
parents c8ed5091ab20
children 10edd71ae8cb
files extra/control-oo/inst/care.m extra/control-oo/inst/dare.m extra/control-oo/src/Makefile extra/control-oo/src/slsb02od.cc
diffstat 4 files changed, 352 insertions(+), 157 deletions(-) [+]
line wrap: on
line diff
--- a/extra/control-oo/inst/care.m	Fri Feb 12 22:52:54 2010 +0000
+++ b/extra/control-oo/inst/care.m	Sat Feb 13 13:06:39 2010 +0000
@@ -1,4 +1,4 @@
-## Copyright (C) 2009   Lukas F. Reichlin
+## Copyright (C) 2009 - 2010   Lukas F. Reichlin
 ##
 ## This file is part of LTI Syncope.
 ##
@@ -21,56 +21,55 @@
 ## Return unique stabilizing solution x of the continuous-time
 ## riccati equation as well as the closed-loop poles l and the
 ## corresponding gain matrix g.
+## Uses SLICOT SB02OD by courtesy of NICONET e.V.
+## <http://www.slicot.org>
 ## @end deftypefn
 
 ## Author: Lukas Reichlin <lukas.reichlin@gmail.com>
 ## Created: November 2009
-## Version: 0.1
+## Version: 0.2
 
-function [x, l, g] = care (a, b, q, r, s = [], opt = "B")
+function [x, l, g] = care (a, b, q, r, s = [])
 
-  if (nargin < 4 || nargin > 6)
+  if (nargin < 4 || nargin > 5)
     print_usage ();
   endif
 
-  if (nargin == 6)
-    if (ischar (opt))
-      opt = upper (opt(1));
-      if (opt != "B" && opt != N && opt != "P" && opt != "S")
-        warning ("dare: opt has invalid value ""%s""; setting to ""B""", opt);
-        opt = "B";
-      endif
-    else
-      warning ("dare: invalid argument opt, setting to ""B""");
-      opt = "B";
-    endif
+  asize = issquare (a);
+  [brows, bcols] = size (b);
+  qsize = issquare (q);
+  rsize = issquare (r);
+
+  if (! asize)
+    error ("care: a is not square");
   endif
 
-  [n, m] = size (b);
-  p = issquare (q);
-  m1 = issquare (r);
-
-  if (! m1)
-    error ("care: r is not square");
-  elseif (m1 != m)
-    error ("care: b, r are not conformable");
+  if (! qsize)
+    error ("care: q is not square");
   endif
 
-  if (! p)
-    q = q' * q;
+  if (! rsize)
+    error ("care: r is not square");
+  endif
+  
+  if (asize != brows)
+    error ("care: a, b are not conformable");
+  endif
+  
+  if (rsize != bcols)
+    error ("care: b, r are not conformable");
   endif
 
   ## incorporate cross term into a and q
   if (isempty (s))
-    s = zeros (n, m);
     ao = a;
     qo = q;
   else
-    [n2, m2] = size (s);
+    [srows, scols] = size (s);  % [n2, m2]
 
-    if (n2 != n || m2 != m)
+    if (srows != brows || scols != bcols)
       error ("care: s (%dx%d) must be identically dimensioned with b (%dx%d)",
-              n2, m2, n, m);
+              srows, scols, brows, bcols);
     endif
 
     ao = a - (b/r)*s';
@@ -86,26 +85,38 @@
   dflag = isdetectable (ao, qo, [], 0);
 
   if (dflag == 0)
-    warning ("care: a and q not detectable");
+    warning ("care: (a,q) not detectable");
   elseif (dflag == -1)
-    error ("care: a and q have poles on imaginary axis");
+    error ("care: (a,q) has poles on imaginary axis");
   endif
 
   ## to allow lqe design, don't force
   ## qo to be positive semi-definite
 
+  ## checking positive definiteness
   if (isdefinite (r) <= 0)
     error ("care: r must be positive definite");
   endif
 
-  ## unique stabilizing solution
-  x = are (ao, (b/r)*b', qo, opt);
-
-  ## corresponding gain matrix
-  g = r \ (b'*x + s');
+  ## solve the riccati equation
+  if (isempty (s))
+    ## unique stabilizing solution
+    x = slsb02od (a, b, q, r, b, false, false);
+    
+    ## corresponding gain matrix
+    g = r \ (b'*x);
+  else
+    ## unique stabilizing solution
+    x = slsb02od (a, b, q, r, s, false, true);
+    
+    ## corresponding gain matrix
+    g = r \ (b'*x + s');
+  endif
 
   ## closed-loop poles
   l = eig (a - b*g);
+  
+  ## TODO: use alphar, alphai and beta from SB02OD
 
 endfunction
 
@@ -133,4 +144,33 @@
 %!
 %!assert (x, xe, 1e-4);
 %!assert (l, le, 1e-4);
+%!assert (g, ge, 1e-4);
+
+%!shared x, l, g, xe, le, ge
+%! a = [ 0.0  1.0
+%!       0.0  0.0];
+%!
+%! b = [ 0.0
+%!       1.0];
+%!
+%! c = [ 1.0  0.0
+%!       0.0  1.0
+%!       0.0  0.0];
+%!
+%! d = [ 0.0
+%!       0.0
+%!       1.0];
+%!
+%! [x, l, g] = care (a, b, c'*c, d'*d);
+%!
+%! xe = [ 1.7321   1.0000
+%!        1.0000   1.7321];
+%!
+%! le = [-0.8660 + 0.5000i
+%!       -0.8660 - 0.5000i];
+%!
+%! ge = [ 1.0000   1.7321];
+%!
+%!assert (x, xe, 1e-4);
+%!assert (l, le, 1e-4);
 %!assert (g, ge, 1e-4);
\ No newline at end of file
--- a/extra/control-oo/inst/dare.m	Fri Feb 12 22:52:54 2010 +0000
+++ b/extra/control-oo/inst/dare.m	Sat Feb 13 13:06:39 2010 +0000
@@ -1,135 +1,81 @@
-## Copyright (C) 1996, 1997, 2000, 2002, 2003, 2004, 2005, 2006, 2007
-##               Auburn University.  All rights reserved.
+## Copyright (C) 2009 - 2010   Lukas F. Reichlin
 ##
+## This file is part of LTI Syncope.
 ##
-## This program 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 3 of the License, or (at
-## your option) any later version.
+## LTI Syncope 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 3 of the License, or
+## (at your option) any later version.
 ##
-## This program 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.
+## LTI Syncope 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 this program; see the file COPYING.  If not, see
-## <http://www.gnu.org/licenses/>.
+## along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 ## -*- texinfo -*-
 ## @deftypefn {Function File} {[@var{x}, @var{l}, @var{g}] =} dare (@var{a}, @var{b}, @var{q}, @var{r})
 ## @deftypefnx {Function File} {[@var{x}, @var{l}, @var{g}] =} dare (@var{a}, @var{b}, @var{q}, @var{r}, @var{s})
-## @deftypefnx {Function File} {[@var{x}, @var{l}, @var{g}] =} dare (@var{a}, @var{b}, @var{q}, @var{r}, @var{s}, @var{opt})
-## Return the solution, @var{x} of the discrete-time algebraic Riccati
-## equation
-## @iftex
-## @tex
-## $$
-## A^TXA - X + A^TXB  (R + B^TXB)^{-1} B^TXA + Q = 0
-## $$
-## @end tex
-## @end iftex
-## @ifinfo
-## @example
-## a' x a - x + a' x b (r + b' x b)^(-1) b' x a + q = 0
-## @end example
-## @end ifinfo
-## @noindent
-##
-## @strong{Inputs}
-## @table @var
-## @item a
-## @var{n} by @var{n} matrix;
-##
-## @item b
-## @var{n} by @var{m} matrix;
-##
-## @item q
-## @var{n} by @var{n} matrix, symmetric positive semidefinite, or a @var{p} by @var{n} matrix,
-## In the latter case @math{q:=q'*q} is used;
-##
-## @item r
-## @var{m} by @var{m}, symmetric positive definite (invertible);
-##
-## @item opt
-## (optional argument; default = @code{"B"}):
-## String option passed to @code{balance} prior to ordered @var{QZ} decomposition.
-## @end table
-##
-## @strong{Output}
-## @table @var
-## @item x
-## solution of @acronym{DARE}.
-## @end table
-##
-## @strong{Method}
-## Generalized eigenvalue approach (Van Dooren; @acronym{SIAM} J.
-##  Sci. Stat. Comput., Vol 2) applied  to the appropriate symplectic pencil.
-##
-##  See also: Ran and Rodman, @cite{Stable Hermitian Solutions of Discrete
-##  Algebraic Riccati Equations}, Mathematics of Control, Signals and
-##  Systems, Vol 5, no 2 (1992), pp 165--194.
-## @seealso{balance, are}
+## Return unique stabilizing solution x of the discrete-time
+## riccati equation as well as the closed-loop poles l and the
+## corresponding gain matrix g.
+## Uses SLICOT SB02OD by courtesy of NICONET e.V.
+## <http://www.slicot.org>
 ## @end deftypefn
 
-## Author: A. S. Hodel <a.s.hodel@eng.auburn.edu>
-## Created: August 1993
-## Adapted-By: jwe
+## Author: Lukas Reichlin <lukas.reichlin@gmail.com>
+## Created: October 2009
+## Version: 0.2
 
-## Adapted-By: Lukas Reichlin <lukas.reichlin@gmail.com>
-## Date: October 2009
-## Version: 0.1
+function [x, l, g] = dare (a, b, q, r, s = [])
 
-function [x, l, g] = dare (a, b, q, r, s = [], opt = "B")
-
-  if (nargin < 4 || nargin > 6)
+  if (nargin < 4 || nargin > 5)
     print_usage ();
   endif
 
-  if (nargin == 6)
-    if (ischar (opt))
-      opt = upper (opt(1));
-      if (opt != "B" && opt != N && opt != "P" && opt != "S")
-        warning ("dare: opt has invalid value ""%s""; setting to ""B""", opt);
-        opt = "B";
-      endif
-    else
-      warning ("dare: invalid argument opt, setting to ""B""");
-      opt = "B";
-    endif
+  asize = issquare (a);
+  [brows, bcols] = size (b);
+  qsize = issquare (q);
+  rsize = issquare (r);
+
+  if (! asize)
+    error ("dare: a is not square");
   endif
 
-  [n, m] = size (b);
-  p = issquare (q);
-  m1 = issquare (r);
+  if (! qsize)
+    error ("dare: q is not square");
+  endif
 
-  if (! m1)
+  if (! rsize)
     error ("dare: r is not square");
-  elseif (m1 != m)
+  endif
+  
+  if (asize != brows)
+    error ("dare: a, b are not conformable");
+  endif
+  
+  if (rsize != bcols)
     error ("dare: b, r are not conformable");
   endif
 
-  if (! p)
-    q = q' * q;
-  endif
-
-  ## incorporate cross term into a and q.
+  ## incorporate cross term into a and q
   if (isempty (s))
-    s = zeros (n, m);
     ao = a;
     qo = q;
   else
-    [n2, m2] = size (s);
+    [srows, scols] = size (s);  % [n2, m2]
 
-    if (n2 != n || m2 != m)
+    if (srows != brows || scols != bcols)
       error ("dare: s (%dx%d) must be identically dimensioned with b (%dx%d)",
-              n2, m2, n, m);
+              srows, scols, brows, bcols);
     endif
 
     ao = a - (b/r)*s';
     qo = q - (s/r)*s';
   endif
-
+  
   ## check stabilizability
   if (! isstabilizable (ao, b, [], 1))
     error ("dare: a and b not stabilizable");
@@ -144,35 +90,35 @@
     error ("dare: (a,q) has non-minimal modes near unit circle");
   endif
 
-  ## Checking positive definiteness
+  ## to allow lqe design, don't force
+  ## qo to be positive semi-definite
+
+  ## checking positive definiteness
   if (isdefinite (r) <= 0)
     error ("dare: r must be positive definite");
   endif
 
-  ## if (isdefinite (qo) < 0)
-  ##   error ("dare: q not positive semidefinite");
-  ## endif
-
   ## solve the riccati equation
-  s1 = [ ao, zeros(n);
-        -qo,  eye(n) ];
-  s2 = [ eye(n),  (b/r)*b';
-        zeros(n),    ao'  ];
-
-  [c, d, s1, s2] = balance (s1, s2, opt);
-  [aa, bb, u, lam] = qz (s1, s2, "S");
-
-  u = d*u;
-  n1 = n+1;
-  n2 = 2*n;
-
-  ## unique stabilizing solution
-  x = u(n1:n2, 1:n) / u(1:n, 1:n);
-
-  ## corresponding gain matrix
-  g = (r+b'*x*b) \ (b'*x*a + s');
+  if (isempty (s))
+    ## unique stabilizing solution
+    x = slsb02od (a, b, q, r, b, true, false);
+    
+    ## corresponding gain matrix
+    g = (r+b'*x*b) \ (b'*x*a);
+  else
+    ## unique stabilizing solution
+    x = slsb02od (a, b, q, r, s, true, true);
+    
+    ## corresponding gain matrix
+    g = (r+b'*x*b) \ (b'*x*a + s');
+  endif
 
   ## closed-loop poles
   l = eig (a - b*g);
+  
+  ## TODO: use alphar, alphai and beta from SB02OD
 
 endfunction
+
+
+## TODO: add a test
\ No newline at end of file
--- a/extra/control-oo/src/Makefile	Fri Feb 12 22:52:54 2010 +0000
+++ b/extra/control-oo/src/Makefile	Sat Feb 13 13:06:39 2010 +0000
@@ -1,6 +1,6 @@
 all: slab08nd.oct slab13dd.oct slsb10hd.oct slsb10ed.oct slab13bd.oct \
      slsb01bd.oct slsb10fd.oct slsb10dd.oct slsb03md.oct slsb04md.oct \
-     slsb04qd.oct slsg03ad.oct
+     slsb04qd.oct slsg03ad.oct slsb02od.oct
 
 # transmission zeros of state-space models
 slab08nd.oct: slab08nd.cc
@@ -91,5 +91,11 @@
               SG03AD.f MB01RW.f MB01RD.f SG03AX.f SG03AY.f \
               MB02UU.f MB02UV.f
 
+# algebraic Riccati equations
+slsb02od.oct: slsb02od.cc
+	mkoctfile slsb02od.cc \
+              SB02OD.f SB02OU.f SB02OV.f SB02OW.f SB02OY.f \
+              SB02MR.f SB02MV.f
+
 clean:
 	rm *.o core octave-core *.oct *~
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/control-oo/src/slsb02od.cc	Sat Feb 13 13:06:39 2010 +0000
@@ -0,0 +1,203 @@
+/*
+
+Copyright (C) 2010   Lukas F. Reichlin
+
+This file is part of LTI Syncope.
+
+LTI Syncope 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 3 of the License, or
+(at your option) any later version.
+
+LTI Syncope 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+Solve algebraic Riccati equation.
+Uses SLICOT SB02OD by courtesy of NICONET e.V.
+<http://www.slicot.org>
+
+Author: Lukas Reichlin <lukas.reichlin@gmail.com>
+Created: February 2010
+Version: 0.1
+
+*/
+
+#include <octave/oct.h>
+#include <f77-fcn.h>
+
+extern "C"
+{ 
+    int F77_FUNC (sb02od, SB02OD)
+                 (char& DICO, char& JOBB,
+                  char& FACT, char& UPLO,
+                  char& JOBL, char& SORT,
+                  int& N, int& M, int& P,
+                  double* A, int& LDA,
+                  double* B, int& LDB,
+                  double* Q, int& LDQ,
+                  double* R, int& LDR,
+                  double* L, int& LDL,
+                  double& RCOND,
+                  double* X, int& LDX,
+                  double* ALFAR, double* ALFAI,
+                  double* BETA,
+                  double* S, int& LDS,
+                  double* T, int& LDT,
+                  double* U, int& LDU,
+                  double& TOL,
+                  int* IWORK,
+                  double* DWORK, int& LDWORK,
+                  bool* BWORK,
+                  int& INFO);
+}
+
+int max (int a, int b)
+{
+    if (a > b)
+        return a;
+    else
+        return b;
+}
+
+int max (int a, int b, int c)
+{
+    int d = max (a, b);
+    
+    return max (c, d);
+}
+
+int max (int a, int b, int c, int d)
+{
+    int e = max (a, b);
+    int f = max (c, d);
+    
+    return max (e, f);
+}
+     
+DEFUN_DLD (slsb02od, args, nargout, "Slicot SB02OD Release 5.0")
+{
+    int nargin = args.length ();
+    octave_value_list retval;
+    
+    if (nargin != 7)
+    {
+        print_usage ();
+    }
+    else
+    {
+        // arguments in
+        char dico;
+        char jobb = 'B';
+        char fact = 'N';
+        char uplo = 'U';
+        char jobl;
+        char sort = 'S';
+        
+        NDArray a = args(0).array_value ();
+        NDArray b = args(1).array_value ();
+        NDArray q = args(2).array_value ();
+        NDArray r = args(3).array_value ();
+        NDArray l = args(4).array_value ();
+        int digital = args(5).int_value ();
+        int ijobl = args(6).int_value ();
+
+        if (digital == 0)
+            dico = 'C';
+        else
+            dico = 'D';
+            
+        if (ijobl == 0)
+            jobl = 'Z';
+        else
+            jobl = 'N';
+        
+        int n = a.rows ();      // n: number of states
+        int m = b.columns ();   // m: number of inputs
+        int p = 0;              // p: number of outputs, not used because FACT = 'N'
+        
+        int lda = max (1, n);
+        int ldb = max (1, n);
+        int ldq = max (1, n);
+        int ldr = max (1, m);
+        int ldl = max (1, n);
+        
+        // arguments out
+        double rcond;
+        
+        int ldx = max (1, n);
+        dim_vector dv (2);
+        dv(0) = ldx;
+        dv(1) = n;
+        NDArray x (dv);
+        
+        // unused output arguments
+        OCTAVE_LOCAL_BUFFER (double, alfar, 2*n);
+        OCTAVE_LOCAL_BUFFER (double, alfai, 2*n);
+        OCTAVE_LOCAL_BUFFER (double, beta, 2*n);
+        
+        int lds = max (1, 2*n + m);
+        OCTAVE_LOCAL_BUFFER (double, s, lds*lds);
+        
+        int ldt = max (1, 2*n + m);
+        OCTAVE_LOCAL_BUFFER (double, t, ldt * 2*n);
+        
+        int ldu = max (1, 2*n);
+        OCTAVE_LOCAL_BUFFER (double, u, ldu * 2*n);
+        
+        // tolerance
+        double tol = 0;  // use default value
+        
+        // workspace
+        int liwork = max (1, m, 2*n);
+        OCTAVE_LOCAL_BUFFER (int, iwork, liwork);
+        
+        int ldwork = max (7*(2*n + 1) + 16, 16*n, 2*n + m, 3*m);
+        OCTAVE_LOCAL_BUFFER (double, dwork, ldwork);
+        
+        OCTAVE_LOCAL_BUFFER (bool, bwork, 2*n);
+        
+        // error indicator
+        int info;
+
+
+        // SLICOT routine SB02OD
+        F77_XFCN (sb02od, SB02OD,
+                 (dico, jobb,
+                  fact, uplo,
+                  jobl, sort,
+                  n, m, p,
+                  a.fortran_vec (), lda,
+                  b.fortran_vec (), ldb,
+                  q.fortran_vec (), ldq,
+                  r.fortran_vec (), ldr,
+                  l.fortran_vec (), ldl,
+                  rcond,
+                  x.fortran_vec (), ldx,
+                  alfar, alfai,
+                  beta,
+                  s, lds,
+                  t, ldt,
+                  u, ldu,
+                  tol,
+                  iwork,
+                  dwork, ldwork,
+                  bwork,
+                  info));
+
+        if (f77_exception_encountered)
+            error ("lti: norm: slsb02od: exception in SLICOT subroutine SB02OD");
+            
+        if (info != 0)
+            error ("lti: norm: slsb02od: SB02OD returned info = %d", info);
+        
+        // return value
+        retval(0) = x;
+    }
+    
+    return retval;
+}