source: branches/opt_cond_exp/cppad/local/reverse_sweep.hpp @ 2979

Last change on this file since 2979 was 2979, checked in by bradbell, 7 years ago
  1. Add operator index to trace output.
  2. Check that new operator values have been set and are valid.
  3. Reinitialize cskip_op_ after optimization.

optimize.hpp: Add test of entirely removing an atomic call.

  • Property svn:keywords set to Id
File size: 18.5 KB
Line 
1/* $Id: reverse_sweep.hpp 2979 2013-10-20 18:00:28Z bradbell $ */
2# ifndef CPPAD_REVERSE_SWEEP_INCLUDED
3# define CPPAD_REVERSE_SWEEP_INCLUDED
4
5/* --------------------------------------------------------------------------
6CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-13 Bradley M. Bell
7
8CppAD is distributed under multiple licenses. This distribution is under
9the terms of the
10                    Eclipse Public License Version 1.0.
11
12A copy of this license is included in the COPYING file of this distribution.
13Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
14-------------------------------------------------------------------------- */
15
16
17namespace CppAD { // BEGIN_CPPAD_NAMESPACE
18/*!
19\defgroup reverse_sweep_hpp reverse_sweep.hpp
20\{
21\file reverse_sweep.hpp
22Compute derivatives of arbitrary order Taylor coefficients.
23*/
24
25/*
26\def CPPAD_ATOMIC_CALL
27This avoids warnings when NDEBUG is defined and user_ok is not used.
28If \c NDEBUG is defined, this resolves to
29\code
30        user_atom->reverse
31\endcode
32otherwise, it respolves to
33\code
34        user_ok = user_atom->reverse
35\endcode
36This maco is undefined at the end of this file to facillitate is
37use with a different definition in other files.
38*/
39# ifdef NDEBUG
40# define CPPAD_ATOMIC_CALL user_atom->reverse
41# else
42# define CPPAD_ATOMIC_CALL user_ok = user_atom->reverse
43# endif
44
45/*!
46\def CPPAD_REVERSE_SWEEP_TRACE
47This value is either zero or one.
48Zero is the normal operational value.
49If it is one, a trace of every reverse_sweep computation is printed.
50*/
51# define CPPAD_REVERSE_SWEEP_TRACE 0
52
53/*!
54Compute derivative of arbitrary order forward mode Taylor coefficients.
55
56\tparam Base
57base type for the operator; i.e., this operation sequence was recorded
58using AD< \a Base > and computations by this routine are done using type
59\a Base.
60
61\param d
62is the highest order Taylor coefficients that
63we are computing the derivative of.
64
65\param n
66is the number of independent variables on the tape.
67
68\param numvar
69is the total number of variables on the tape.
70This is also equal to the number of rows in the matrix \a Taylor; i.e.,
71Rec->num_rec_var().
72
73\param Rec
74The information stored in \a Rec
75is a recording of the operations corresponding to the function
76\f[
77        F : {\bf R}^n \rightarrow {\bf R}^m
78\f]
79where \f$ n \f$ is the number of independent variables and
80\f$ m \f$ is the number of dependent variables.
81We define the function
82\f$ G : {\bf R}^{n \times d} \rightarrow {\bf R} \f$ by
83\f[
84G( u ) = \frac{1}{d !} \frac{ \partial^d }{ \partial t^d }
85\left[
86        \sum_{i=1}^m w_i  F_i ( u^{(0)} + u^{(1)} t + \cdots + u^{(d)} t^d )
87\right]_{t=0}
88\f]
89Note that the scale factor  1 / a d  converts
90the \a d-th partial derivative to the \a d-th order Taylor coefficient.
91This routine computes the derivative of \f$ G(u) \f$
92with respect to all the Taylor coefficients
93\f$ u^{(k)} \f$ for \f$ k = 0 , ... , d \f$.
94The vector \f$ w \in {\bf R}^m \f$, and
95value of \f$ u \in {\bf R}^{n \times d} \f$
96at which the derivative is computed,
97are defined below.
98\n
99\n
100The object \a Rec is effectly constant.
101There is an exception to this,
102while palying back the tape
103the object \a Rec holds information about the current location
104with in the tape and this changes during palyback.
105
106\param J
107Is the number of columns in the coefficient matrix \a Taylor.
108This must be greater than or equal \a d + 1.
109
110\param Taylor
111For i = 1 , ... , \a numvar, and for k = 0 , ... , \a d,
112\a Taylor [ i * J + k ]
113is the k-th order Taylor coefficient corresponding to
114variable with index i on the tape.
115The value \f$ u \in {\bf R}^{n \times d} \f$,
116at which the derivative is computed,
117is defined by
118\f$ u_j^{(k)} \f$ = \a Taylor [ j * J + k ]
119for j = 1 , ... , \a n, and for k = 0 , ... , \a d.
120
121\param K
122Is the number of columns in the partial derivative matrix \a Partial.
123It must be greater than or equal \a d + 1.
124
125\param Partial
126\b Input:
127The last \f$ m \f$ rows of \a Partial are inputs.
128The vector \f$ v \f$, used to define \f$ G(u) \f$,
129is specified by these rows.
130For i = 0 , ... , m - 1, \a Partial [ ( \a numvar - m + i ) * K + d ] = v_i.
131For i = 0 , ... , m - 1 and for k = 0 , ... , d - 1,
132\a Partial [ ( \a numvar - m + i ) * K + k ] = 0.
133\n
134\n
135\b Temporary:
136For i = n+1 , ... , \a numvar - 1 and for k = 0 , ... , d,
137the value of \a Partial [ i * K + k ] is used for temporary work space
138and its output value is not defined.
139\n
140\n
141\b Output:
142For j = 1 , ... , n and for k = 0 , ... , d,
143\a Partial [ j * K + k ]
144is the partial derivative of \f$ G( u ) \f$ with
145respect to \f$ u_j^{(k)} \f$.
146
147\par Assumptions
148The first operator on the tape is a BeginOp,
149and the next \a n operators are InvOp operations for the
150corresponding independent variables.
151*/
152template <class Base>
153void ReverseSweep(
154        size_t                d,
155        size_t                n,
156        size_t                numvar,
157        player<Base>*         Rec,
158        size_t                J,
159        const Base*           Taylor,
160        size_t                K,
161        Base*                 Partial
162)
163{
164        OpCode           op;
165        size_t         i_op;
166        size_t        i_var;
167
168        const addr_t*   arg = CPPAD_NULL;
169
170        // check numvar argument
171        CPPAD_ASSERT_UNKNOWN( Rec->num_rec_var() == numvar );
172        CPPAD_ASSERT_UNKNOWN( numvar > 0 );
173
174        // length of the parameter vector (used by CppAD assert macros)
175        const size_t num_par = Rec->num_rec_par();
176
177        // pointer to the beginning of the parameter vector
178        const Base* parameter = CPPAD_NULL;
179        if( num_par > 0 )
180                parameter = Rec->GetPar();
181
182        // work space used by UserOp.
183        const size_t user_k  = d;    // highest order we are differentiating
184        const size_t user_k1 = d+1;  // number of orders for this calculation
185        vector<size_t> user_ix;      // variable indices for argument vector
186        vector<Base> user_tx;        // argument vector Taylor coefficients
187        vector<Base> user_ty;        // result vector Taylor coefficients
188        vector<Base> user_px;        // partials w.r.t argument vector
189        vector<Base> user_py;        // partials w.r.t. result vector
190        size_t user_index = 0;       // indentifier for this atomic operation
191        size_t user_id    = 0;       // user identifier for this call to operator
192        size_t user_i     = 0;       // index in result vector
193        size_t user_j     = 0;       // index in argument vector
194        size_t user_m     = 0;       // size of result vector
195        size_t user_n     = 0;       // size of arugment vector
196        //
197        atomic_base<Base>* user_atom = CPPAD_NULL; // user's atomic op calculator
198# ifndef NDEBUG
199        bool               user_ok   = false;      // atomic op return value
200# endif
201        //
202        // next expected operator in a UserOp sequence
203        enum { user_start, user_arg, user_ret, user_end } user_state = user_end;
204
205        // temporary indices
206        size_t j, ell;
207
208        // Initialize
209        Rec->start_reverse(op, arg, i_op, i_var);
210        CPPAD_ASSERT_UNKNOWN( op == EndOp );
211# if CPPAD_REVERSE_SWEEP_TRACE
212        std::cout << std::endl;
213# endif
214        while(op != BeginOp )
215        {       // next op
216                Rec->next_reverse(op, arg, i_op, i_var);
217# ifndef NDEBUG
218                if( i_op <= n )
219                {       CPPAD_ASSERT_UNKNOWN((op == InvOp) | (op == BeginOp));
220                }
221                else    CPPAD_ASSERT_UNKNOWN((op != InvOp) & (op != BeginOp));
222# endif
223
224                // rest of informaiton depends on the case
225# if CPPAD_REVERSE_SWEEP_TRACE
226                size_t       i_tmp  = i_var;
227                const Base*  Z_tmp  = Taylor + i_var * J;
228                const Base*  pZ_tmp = Partial + i_var * K;
229
230                printOp(
231                        std::cout, 
232                        Rec,
233                        i_op,
234                        i_tmp,
235                        op, 
236                        arg,
237                        d + 1, 
238                        Z_tmp, 
239                        d + 1, 
240                        pZ_tmp
241                );
242# endif
243
244                switch( op )
245                {
246
247                        case AbsOp:
248                        reverse_abs_op(
249                                d, i_var, arg[0], J, Taylor, K, Partial
250                        );
251                        break;
252                        // --------------------------------------------------
253
254                        case AddvvOp:
255                        reverse_addvv_op(
256                                d, i_var, arg, parameter, J, Taylor, K, Partial
257                        );
258                        break;
259                        // --------------------------------------------------
260
261                        case AddpvOp:
262                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
263                        reverse_addpv_op(
264                                d, i_var, arg, parameter, J, Taylor, K, Partial
265                        );
266                        break;
267                        // --------------------------------------------------
268
269                        case AcosOp:
270                        // sqrt(1 - x * x), acos(x)
271                        CPPAD_ASSERT_UNKNOWN( i_var < numvar );
272                        reverse_acos_op(
273                                d, i_var, arg[0], J, Taylor, K, Partial
274                        );
275                        break;
276                        // --------------------------------------------------
277
278                        case AsinOp:
279                        // sqrt(1 - x * x), asin(x)
280                        CPPAD_ASSERT_UNKNOWN( i_var < numvar );
281                        reverse_asin_op(
282                                d, i_var, arg[0], J, Taylor, K, Partial
283                        );
284                        break;
285                        // --------------------------------------------------
286
287                        case AtanOp:
288                        // 1 + x * x, atan(x)
289                        CPPAD_ASSERT_UNKNOWN( i_var < numvar );
290                        reverse_atan_op(
291                                d, i_var, arg[0], J, Taylor, K, Partial
292                        );
293                        break;
294                        // -------------------------------------------------
295
296                        case BeginOp:
297                        CPPAD_ASSERT_NARG_NRES(op, 0, 1);
298                        break;
299                        // --------------------------------------------------
300
301                        case CSumOp:
302                        // CSumOp has a variable number of arguments and
303                        // next_reverse thinks it one has one argument.
304                        // We must inform next_reverse of this special case.
305                        Rec->reverse_csum(op, arg, i_op, i_var);
306                        reverse_csum_op(
307                                d, i_var, arg, K, Partial
308                        );
309                        // end of a cummulative summation
310                        break;
311                        // -------------------------------------------------
312
313                        case CExpOp:
314                        reverse_cond_op(
315                                d, 
316                                i_var, 
317                                arg, 
318                                num_par, 
319                                parameter, 
320                                J, 
321                                Taylor,
322                                K, 
323                                Partial
324                        );
325                        break;
326                        // --------------------------------------------------
327
328                        case ComOp:
329                        break;
330                        // --------------------------------------------------
331
332                        case CosOp:
333                        CPPAD_ASSERT_UNKNOWN( i_var < numvar );
334                        reverse_cos_op(
335                                d, i_var, arg[0], J, Taylor, K, Partial
336                        );
337                        break;
338                        // --------------------------------------------------
339
340                        case CoshOp:
341                        CPPAD_ASSERT_UNKNOWN( i_var < numvar );
342                        reverse_cosh_op(
343                                d, i_var, arg[0], J, Taylor, K, Partial
344                        );
345                        break;
346                        // --------------------------------------------------
347
348                        case DisOp:
349                        // Derivative of discrete operation is zero so no
350                        // contribution passes through this operation.
351                        break;
352                        // --------------------------------------------------
353
354                        case DivvvOp:
355                        reverse_divvv_op(
356                                d, i_var, arg, parameter, J, Taylor, K, Partial
357                        );
358                        break;
359                        // --------------------------------------------------
360
361                        case DivpvOp:
362                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
363                        reverse_divpv_op(
364                                d, i_var, arg, parameter, J, Taylor, K, Partial
365                        );
366                        break;
367                        // --------------------------------------------------
368
369                        case DivvpOp:
370                        CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par );
371                        reverse_divvp_op(
372                                d, i_var, arg, parameter, J, Taylor, K, Partial
373                        );
374                        break;
375                        // --------------------------------------------------
376
377                        case ExpOp:
378                        reverse_exp_op(
379                                d, i_var, arg[0], J, Taylor, K, Partial
380                        );
381                        break;
382                        // --------------------------------------------------
383                        case LdpOp:
384                        reverse_load_op(
385                                op, d, i_var, arg, J, Taylor, K, Partial
386                        );
387                        break;
388                        // -------------------------------------------------
389
390                        case LdvOp:
391                        reverse_load_op(
392                                op, d, i_var, arg, J, Taylor, K, Partial
393                        );
394                        break;
395                        // -------------------------------------------------
396
397                        case InvOp:
398                        break;
399                        // --------------------------------------------------
400
401                        case LogOp:
402                        reverse_log_op(
403                                d, i_var, arg[0], J, Taylor, K, Partial
404                        );
405                        break;
406                        // --------------------------------------------------
407
408                        case MulvvOp:
409                        reverse_mulvv_op(
410                                d, i_var, arg, parameter, J, Taylor, K, Partial
411                        );
412                        break;
413                        // --------------------------------------------------
414
415                        case MulpvOp:
416                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
417                        reverse_mulpv_op(
418                                d, i_var, arg, parameter, J, Taylor, K, Partial
419                        );
420                        break;
421                        // --------------------------------------------------
422
423                        case ParOp:
424                        break;
425                        // --------------------------------------------------
426
427                        case PowvpOp:
428                        CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par );
429                        reverse_powvp_op(
430                                d, i_var, arg, parameter, J, Taylor, K, Partial
431                        );
432                        break;
433                        // -------------------------------------------------
434
435                        case PowpvOp:
436                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
437                        reverse_powpv_op(
438                                d, i_var, arg, parameter, J, Taylor, K, Partial
439                        );
440                        break;
441                        // -------------------------------------------------
442
443                        case PowvvOp:
444                        reverse_powvv_op(
445                                d, i_var, arg, parameter, J, Taylor, K, Partial
446                        );
447                        break;
448                        // --------------------------------------------------
449
450                        case PriOp:
451                        // no result so nothing to do
452                        break;
453                        // --------------------------------------------------
454
455                        case SignOp:
456                        CPPAD_ASSERT_UNKNOWN( i_var < numvar );
457                        reverse_sign_op(
458                                d, i_var, arg[0], J, Taylor, K, Partial
459                        );
460                        break;
461                        // -------------------------------------------------
462
463                        case SinOp:
464                        CPPAD_ASSERT_UNKNOWN( i_var < numvar );
465                        reverse_sin_op(
466                                d, i_var, arg[0], J, Taylor, K, Partial
467                        );
468                        break;
469                        // -------------------------------------------------
470
471                        case SinhOp:
472                        CPPAD_ASSERT_UNKNOWN( i_var < numvar );
473                        reverse_sinh_op(
474                                d, i_var, arg[0], J, Taylor, K, Partial
475                        );
476                        break;
477                        // --------------------------------------------------
478
479                        case SqrtOp:
480                        reverse_sqrt_op(
481                                d, i_var, arg[0], J, Taylor, K, Partial
482                        );
483                        break;
484                        // --------------------------------------------------
485
486                        case StppOp:
487                        break;
488                        // --------------------------------------------------
489
490                        case StpvOp:
491                        break;
492                        // -------------------------------------------------
493
494                        case StvpOp:
495                        break;
496                        // -------------------------------------------------
497
498                        case StvvOp:
499                        break;
500                        // --------------------------------------------------
501
502                        case SubvvOp:
503                        reverse_subvv_op(
504                                d, i_var, arg, parameter, J, Taylor, K, Partial
505                        );
506                        break;
507                        // --------------------------------------------------
508
509                        case SubpvOp:
510                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
511                        reverse_subpv_op(
512                                d, i_var, arg, parameter, J, Taylor, K, Partial
513                        );
514                        break;
515                        // --------------------------------------------------
516
517                        case SubvpOp:
518                        CPPAD_ASSERT_UNKNOWN( size_t(arg[1]) < num_par );
519                        reverse_subvp_op(
520                                d, i_var, arg, parameter, J, Taylor, K, Partial
521                        );
522                        break;
523                        // -------------------------------------------------
524
525                        case TanOp:
526                        CPPAD_ASSERT_UNKNOWN( i_var < numvar );
527                        reverse_tan_op(
528                                d, i_var, arg[0], J, Taylor, K, Partial
529                        );
530                        break;
531                        // -------------------------------------------------
532
533                        case TanhOp:
534                        CPPAD_ASSERT_UNKNOWN( i_var < numvar );
535                        reverse_tanh_op(
536                                d, i_var, arg[0], J, Taylor, K, Partial
537                        );
538                        break;
539                        // --------------------------------------------------
540
541                        case UserOp:
542                        // start or end an atomic operation sequence
543                        CPPAD_ASSERT_UNKNOWN( NumRes( UserOp ) == 0 );
544                        CPPAD_ASSERT_UNKNOWN( NumArg( UserOp ) == 4 );
545                        if( user_state == user_end )
546                        {       user_index = arg[0];
547                                user_id    = arg[1];
548                                user_n     = arg[2];
549                                user_m     = arg[3];
550                                user_atom  = atomic_base<Base>::class_object(user_index);
551# ifndef NDEBUG
552                                if( user_atom == CPPAD_NULL )
553                                {       std::string msg = 
554                                                atomic_base<Base>::class_name(user_index)
555                                                + ": atomic_base function has been deleted";
556                                        CPPAD_ASSERT_KNOWN(false, msg.c_str() );
557                                }
558# endif
559                                if(user_ix.size() != user_n)
560                                        user_ix.resize(user_n);
561                                if(user_tx.size() != user_n * user_k1)
562                                {       user_tx.resize(user_n * user_k1);
563                                        user_px.resize(user_n * user_k1);
564                                }
565                                if(user_ty.size() != user_m * user_k1)
566                                {       user_ty.resize(user_m * user_k1);
567                                        user_py.resize(user_m * user_k1);
568                                }
569                                user_j     = user_n;
570                                user_i     = user_m;
571                                user_state = user_ret;
572                        }
573                        else
574                        {       CPPAD_ASSERT_UNKNOWN( user_state == user_start );
575                                CPPAD_ASSERT_UNKNOWN( user_index == size_t(arg[0]) );
576                                CPPAD_ASSERT_UNKNOWN( user_id    == size_t(arg[1]) );
577                                CPPAD_ASSERT_UNKNOWN( user_n     == size_t(arg[2]) );
578                                CPPAD_ASSERT_UNKNOWN( user_m     == size_t(arg[3]) );
579
580                                // call users function for this operation
581                                user_atom->set_id(user_id);
582                                CPPAD_ATOMIC_CALL(
583                                        user_k, user_tx, user_ty, user_px, user_py
584                                );
585# ifndef NDEBUG
586                                if( ! user_ok )
587                                {       std::string msg = 
588                                                atomic_base<Base>::class_name(user_index)
589                                                + ": atomic_base.reverse: returned false";
590                                        CPPAD_ASSERT_KNOWN(false, msg.c_str() );
591                                }
592# endif
593                                for(j = 0; j < user_n; j++) if( user_ix[j] > 0 )
594                                {       for(ell = 0; ell < user_k1; ell++)
595                                                Partial[user_ix[j] * K + ell] +=
596                                                        user_px[j * user_k1 + ell];
597                                }
598                                user_state = user_end;
599                        }
600                        break;
601
602                        case UsrapOp:
603                        // parameter argument in an atomic operation sequence
604                        CPPAD_ASSERT_UNKNOWN( user_state == user_arg );
605                        CPPAD_ASSERT_UNKNOWN( 0 < user_j && user_j <= user_n );
606                        CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 );
607                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
608                        --user_j;
609                        user_ix[user_j] = 0;
610                        user_tx[user_j * user_k1 + 0] = parameter[ arg[0]];
611                        for(ell = 1; ell < user_k1; ell++)
612                                user_tx[user_j * user_k1 + ell] = Base(0.);
613                       
614                        if( user_j == 0 )
615                                user_state = user_start;
616                        break;
617
618                        case UsravOp:
619                        // variable argument in an atomic operation sequence
620                        CPPAD_ASSERT_UNKNOWN( user_state == user_arg );
621                        CPPAD_ASSERT_UNKNOWN( 0 < user_j && user_j <= user_n );
622                        CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 );
623                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) <= i_var );
624                        CPPAD_ASSERT_UNKNOWN( 0 < arg[0] );
625                        --user_j;
626                        user_ix[user_j] = arg[0];
627                        for(ell = 0; ell < user_k1; ell++)
628                                user_tx[user_j*user_k1 + ell] = Taylor[ arg[0] * J + ell];
629                        if( user_j == 0 )
630                                user_state = user_start;
631                        break;
632
633                        case UsrrpOp:
634                        // parameter result in an atomic operation sequence
635                        CPPAD_ASSERT_UNKNOWN( user_state == user_ret );
636                        CPPAD_ASSERT_UNKNOWN( 0 < user_i && user_i <= user_m );
637                        CPPAD_ASSERT_UNKNOWN( NumArg(op) == 1 );
638                        CPPAD_ASSERT_UNKNOWN( size_t(arg[0]) < num_par );
639                        --user_i;
640                        for(ell = 0; ell < user_k1; ell++)
641                        {       user_py[user_i * user_k1 + ell] = Base(0.);
642                                user_ty[user_i * user_k1 + ell] = Base(0.);
643                        }
644                        user_ty[user_i * user_k1 + 0] = parameter[ arg[0] ];
645                        if( user_i == 0 )
646                                user_state = user_arg;
647                        break;
648
649                        case UsrrvOp:
650                        // variable result in an atomic operation sequence
651                        CPPAD_ASSERT_UNKNOWN( user_state == user_ret );
652                        CPPAD_ASSERT_UNKNOWN( 0 < user_i && user_i <= user_m );
653                        --user_i;
654                        for(ell = 0; ell < user_k1; ell++)
655                        {       user_py[user_i * user_k1 + ell] =
656                                                Partial[i_var * K + ell];
657                                user_ty[user_i * user_k1 + ell] =
658                                                Taylor[i_var * J + ell];
659                        }
660                        if( user_i == 0 )
661                                user_state = user_arg;
662                        break;
663                        // ------------------------------------------------------------
664
665                        default:
666                        CPPAD_ASSERT_UNKNOWN(false);
667                }
668        }
669# if CPPAD_REVERSE_SWEEP_TRACE
670        std::cout << std::endl;
671# endif
672        // values corresponding to BeginOp
673        CPPAD_ASSERT_UNKNOWN( i_op == 0 );
674        CPPAD_ASSERT_UNKNOWN( i_var == 0 );
675}
676
677/*! \} */
678} // END_CPPAD_NAMESPACE
679
680// preprocessor symbols that are local to this file
681# undef CPPAD_REVERSE_SWEEP_TRACE
682# undef CPPAD_ATOMIC_CALL
683
684# endif
Note: See TracBrowser for help on using the repository browser.