# <PLAINTEXT>
##############################################################################
##PROCEDURE(notest)     SpringIntegrate
##AUTHOR                Allan Wittkopf
##DATE                  December 3, 2005
##
##CALLINGSEQ
##- \`dsolve/numeric/SpringIntegrate\`(
##      reledge::list,
##      ini::{integer,hfarray},
##      dim::{identical(2),identical(3)},
##      nframes::nonnegint,
##      maxiter::{posint,name}
##  )
##
##PARAMETERS
##- 'reledge' : `list`; this parameter is an ordered list with each entry
##               containing a list of the vertices that the entry corresponding
##               to the current vertex is connected to.
##               For example, for a complete graph of 3 vertices this is
##               [[2,3],[1,3],[2,3]], meaning vertex 1 is connected to 2,3,
##               vertex 2 is connected to 1,3, etc.
##- 'ini' : This parameter has 3 main values; 0 means to have the code
##          generate structured (grid) initial positions for the vertices;
##          1 means to have the code generate random initial positions for
##          the vertices; and the third input form is an array of initial
##          values for the vertices.
##          For the third form, the data must be a #vertex x #dim C_order
##          float[8] rectangular storage rtable (Matrix or Array).
##          Note that the code is designed so that the final values for an
##          animation approx. fill the range [-1..1$dim], so initial positions
##          should be specified to fill the same range, as otherwise the
##          graph will either grow or shrink in an undesirable way during the
##          animation process.
##- 'dim' : This is the number of dimensions in which the graph is to be
##          built, and must be either 2 or 3.
##- 'nframes' : This should be 0 if only the final values are desired
##              (and these are returned in a #vertex x #dim array). For
##              values > 0, the computation proceeds as an animation
##              (3 times as expensive) and returns the data in a
##              #frames x #vertex x #dim array.
##- 'maxiter' : This is an iteration maximum - it serves to limit the amount
##              of work performed for an animation. A value of 20000 is
##              suitable for any graphs up to the 'extremely difficult'
##              level. This can be specified in 2 ways:
##              1) Just pass a value.
##                 This is then a limit.
##              2) Pass a value through an assigned quoted variable.
##                 This method also sets the number of iterations performed
##                 at output time.
##                 **If** the graph did not complete, then this value will
##                 be 1 greater than the input max iter count on output.
##              The max allowed value is 2^31-1.
##
##RETURNS
##- the procedure returns depend upon the parameters. There are the following
##  cases:
##- nframes=0: returns a #vertex x #dim C_order, rectangular, float[8] rtable
##      that represents the final value computed for the graph.
##- nframes=1: returns a #frames x #vertex x #dim C_order, rectangular,
##      float[8] rtable that represents a smooth transition from the initial
##      value to the final value computed for the graph.
##- For both cases if maxiter is a name, and is assigned to a value no greater
##  than the input maxiter value, then the graph has stabilized, and the answer
##  is final. If the value is greater, then the graph did not stabilize in
##  the specified number of steps.
##      
##
##DESCRIPTION
##- The \`dsolve/numeric/SpringIntegrate\` procedure uses numerical methods,
##  combined with a specialized model system, to integrate a graph from
##  either specified, structured, or random initial positions to a final
##  state where the model system is in a steady state (stable).
##- The model system has been designed so that the steady state is visually
##  appealing based on the following 2 criteria: 1) No overlapping vertices;
##  2) When possible, connected vertices are close to each other.
##
##- Note that if the graph is not connected, a steady state can *never* be
##  reached, as vertices repel one another, and edges counter-balance the
##  effect.
##- Note also that the code is not designed to account-for or ignore
##  connections from a vertex to itself, so the code will simply fail in
##  this case.
##
##SUBSECTION The Model
##- The model is basically a second order ODE system force balance model
##  in 'dim' dimensions. The following are the rules:
##- Each vertex exerts an attractive force to any other vertex to which
##  it is attached by an edge. This force is proportional to the distance
##  between the vertices.
##- Each vertex exerts a constant repulsive force to all other vertices,
##  regardless of whether they are connected by an edge.
##- All vertex motion proceeds in the presence of friction that serves to
##  damp oscillations in the model.
##- Finally, there is one other small force used to 'center' the model
##  (a fictitious force that attempts to center the average positions in
##  the model at the origin.
##
##- The differential equation for each vertex can be described as follows:
##  x[i]\'\'=-a\*sum((x[i]-x[j])/(n[i]+n[j]),j=edges[i])-b\*x[i]\'
##         +c\*sum((x[i]-x[j])/|x[i]-x[j]|,j=1..i-1,i+1..n)-0.001\*xav,
##  where the x[i] is a vector for the position of vertex i, n[i]
##  counts the number of edges connected to vertex i, edges[i] gives
##  the other vertex for each edge connected to vertex i, and xav is the
##  average positions over all nodes.
##- The values of the constants 'a','b','c' are internally managed by the
##  code, but have been optimized (via a very lengthy derivation) to provide
##  stabilization of the model in as little time as possible, and to keep
##  the scale uniform when possible.
##- Note that one may question the use of a constant force repulsion, but
##  this is critical for several reasons. One of the two major reasons is that
##  a constant force model allows for cross-overs in 2-d problems (i.e. one
##  vertex can pass right through another). This makes the dependence of the
##  model steady state much less dependent on the initial positions chosen for
##  the model. Clearly an advantage when the initial values are random.
##  Use of an inverse or inverse square repulsion law does not allow this (as
##  it prevents nodes from getting close enough to allow a crossing).
##  The other major reason is efficiency. It was found that for large
##  graphs, use of an inverse law took many times longer to stabilize.
##  The constant force model is closer to a linear model, and as a result
##  has better behavior with respect to integration to a steady state.
##ENDSUBSECTION
##
##SUBSECTION Final Fix-up
##- The model in use has one fatal flaw.
##  Specifically since the repulsive forces between two vertices are
##  constant, it is possible to have overlapping vertices.
##  This is clearly undesirable.
##- To fix this, there is a final phase of the integration that, after
##  the primary steady state is achieved, checks the state for vertices that
##  overlap, or are too close. The criteria for this is a little complex, but
##  is basically concerned with the average of the minimum vertex distances
##  above a fixed bar, and enforcing that the minimum node distance is at
##  least 0.6 times that average.
##- If these are detected, then the constant repulsive force between any pair
##  of close vertices is increased, and a few more integration steps are taken,
##  until a steady state is achieved with no close/overlapping vertices.
##- It should again be noted that an attempt was made to change the model from
##  the current model to an inverse law model *after* the steady state was
##  achieved, but it was found that the desirable steady state was an unstable
##  stationary state for some models of interest under an inverse law, so this
##  was abandoned in favor of the current criteria.
##ENDSUBSECTION
##
##SUBSECTION Numerical Solution of the Model
##- The model is numerically solved using an order 4 Adams solver, utilizing
##  an rk2 method as a startup for the multi-step integration.
##  This was chosen to minimize the number of function evaluations performed
##  by the algorithm (multi-step req. only 1 function eval per step).
##- The step size used depends upon the stability of the model, but given the
##  choices made for optimization of a,b,c, it turns out that the optimal
##  step size is nearly always in the 0.4-0.6 range.
##- Basically the model proceeds by integrating the system forward in time
##  until the distance traveled by the vertices in a single step drops below
##  a fixed minimum value of 1e-8. Of course the first few steps are excluded
##  from the test, as the initial velocities in the model are always zero,
##  and the system takes a little time to get 'up to speed' as it were.
##- Also the second phase of the numerical integration proceeds in much the
##  same manner.
##ENDSUBSECTION
##
##SUBSECTION Animation Details
##- Animations are computed a little bit differently than just the steady
##  state. The algorithm needs to perform up to 3 passes before producing the
##  final animation:
##- Step 1: Integrate system forward in time to reach the steady state, and
##          note the boundaries of the region for the final state.
##          If these boundaries are within 2% of -1..1, -1..1, -1..1 then
##          proceed to step 3, otherwise adjust 'a','b','c' and proceed to
##          step 2.
##- Step 2: Integrate for the adjusted constants.
##- Step 3: Use the computed total distance traveled in the course of the
##          computation to make the animation uniform in the sense:
##          \"the sum of the absolute values of the distance changed between
##           any two consecutive frames is equal\".
##         Note that this is quite relevant, as the rate of change in the
##         model varies massively, and an equi-spaced time sampling does
##         not produce an esthetically pleasing animation.
##- There are disadvantages to the 'total distance traveled' approach when
##  dealing with small graphs containing very few nodes. The animation may
##  not appear very smooth, as initially the model is stabilizing with
##  respect to relative location of the nodes, and the small force that is
##  used to center the graph is much slower acting.
##  This means that the graph may appear to stabilize first by relative
##  position, then slowly shift to being centered.
##  This is something of a judgment call, as for the equal spaced time
##  approach the first few frames of animation will contain nearly all
##  the movement, and the last 20 will be small shifts - this is also ugly
##  but it is ugly for nearly all graphs.
##ENDSUBSECTION
##

`dsolve/numeric/SpringIntegrate` := proc()
local lib;
global `dsolve/numeric/SpringIntegrate`;
	try
		lib := ExternalCalling:-ExternalLibraryName("ode2",'HWFloat');
		`dsolve/numeric/SpringIntegrate` :=
			ExternalCalling:-DefineExternal('hw_SpringIntegrate',lib);
   catch:
      error("external library %1 could not be found/used",lib);
   end try;
   `dsolve/numeric/SpringIntegrate`(args[1..nargs]);
end proc:

#savelib('`dsolve/numeric/SpringIntegrate`');
##############################################################################
