function [net,tr] = atraingd(net,Pd,Tl,Ai,Q,TS,VV,TV)

global nic
global nh1c
global nia
global nh1a
global noa

%TRAINGD Gradient descent backpropagation.
%
%	Syntax
%	
%	  [net,tr] = traingd(net,Pd,Tl,Ai,Q,TS,VV)
%	  info = traingd(code)
%
%	Description
%
%	  TRAINGD is a network training function that updates weight and
%	  bias values according to gradient descent.
%
%	  TRAINGD(NET,Pd,Tl,Ai,Q,TS,VV) takes these inputs,
%	    NET - Neural network.
%	    Pd  - Delayed input vectors.
%	    Tl  - Layer target vectors.
%	    Ai  - Initial input delay conditions.
%	    Q   - Batch size.
%	    TS  - Time steps.
%	    VV  - Either an empty matrix [] or a structure of validation vectors.
%	  and returns:
%	    NET - Trained network.
%	    TR  - Training record of various values over each epoch:
%	          TR.epoch - Epoch number.
%	          TR.perf  - Training performance.
%	          TR.vperf - Validation performance.
%	          TR.tperf - Test performance.
%
%	  Training occurs according to the TRAINGD's training parameters
%	  shown here with their default values:
%	    net.trainParam.epochs      10  Maximum number of epochs to train
%	    net.trainParam.goal         0  Performance goal
%	    net.trainParam.lr        0.01  Learning rate
%	    net.trainParam.max_fail     5  Maximum validation failures
%	    net.trainParam.min_grad 1e-10  Minimum performance gradient
%	    net.trainParam.show        25  Epochs between showing progress
%	    net.trainParam.time       inf  Maximum time to train in seconds
%
%	  Dimensions for these variables are:
%	    Pd - NoxNixTS cell array, each element P{i,j,ts} is a DijxQ matrix.
%	    Tl - NlxTS cell array, each element P{i,ts} is a VixQ matrix.
%		Ai - NlxLD cell array, each element Ai{i,k} is an SixQ matrix.
%	  Where
%	    Ni = net.numInputs
%		Nl = net.numLayers
%		LD = net.numLayerDelays
%	    Ri = net.inputs{i}.size
%	    Si = net.layers{i}.size
%	    Vi = net.targets{i}.size
%	    Dij = Ri * length(net.inputWeights{i,j}.delays)
%
%	  If VV is not [], it must be a structure of validation vectors,
%	    VV.PD - Validation delayed inputs.
%	    VV.Tl - Validation layer targets.
%	    VV.Ai - Validation initial input conditions.
%	    VV.Q  - Validation batch size.
%	    VV.TS - Validation time steps.
%	  which are used to stop training early if the network performance
%	  on the validation vectors fails to improve or remain the same
%	  for MAX_FAIL epochs in a row.
%
%	  TRAINGD(CODE) returns useful information for each CODE string:
%	    'pnames'    - Names of training parameters.
%	    'pdefaults' - Default training parameters.
%
%	Network Use
%
%	  You can create a standard network that uses TRAINGD with
%	  NEWFF, NEWCF, or NEWELM.
%
%	  To prepare a custom network to be trained with TRAINGD:
%	  1) Set NET.trainFcn to 'traingd'.
%	     This will set NET.trainParam to TRAINGD's default parameters.
%	  2) Set NET.trainParam properties to desired values.
%
%	  In either case, calling TRAIN with the resulting network will
%	  train the network with TRAINGD.
%
%	  See NEWFF, NEWCF, and NEWELM for examples.
%
%	Algorithm
%
%	  TRAINGD can train any network as long as its weight, net input,
%	  and transfer functions have derivative functions.
%
%	  Backpropagation is used to calculate derivatives of performance
%	  PERF with respect to the weight and bias variables X.  Each
%	  variable is adjusted according to gradient descent:
%
%	    dX = lr * dperf/dX
%
%	  Training stops when any of these conditions occurs:
%	  1) The maximum number of EPOCHS (repetitions) is reached.
%	  2) The maximum amount of TIME has been exceeded.
%	  3) Performance has been minimized to the GOAL.
%	  4) The performance gradient falls below MINGRAD.
%	  5) Validation performance has increased more than MAX_FAIL times
%	     since the last time it decreased (when using validation).
%
%	See also NEWFF, NEWCF, TRAINGDM, TRAINGDA, TRAINGDX, TRAINLM.

% Mark Beale, 11-31-97
% Copyright (c) 1992-1998 The MathWorks, Inc.
% $Revision: 1.4 $

% FUNCTION INFO
% =============

if isstr(net)
  switch (net)
    case 'pnames',
	  net = {'epochs';'goal';'lr';'max_fail';'min_grad';...
	    'show';'time'};
    case 'pdefaults',
	  trainParam.epochs = 100;
	  trainParam.goal = 0;
	  trainParam.lr = 0.01;
	  trainParam.max_fail = 5;
	  trainParam.min_grad = 1e-10;
	  trainParam.show = 25;
	  trainParam.time = inf;
	  net = trainParam;
    otherwise,
	  error('Unrecognized code.')
  end
  return
end

% CALCULATION
% ===========

% Constants
this = 'TRAINGD';
epochs = net.trainParam.epochs;
goal = net.trainParam.goal;
lr = net.trainParam.lr;
max_fail = net.trainParam.max_fail;
min_grad = net.trainParam.min_grad;
show = net.trainParam.show;
time = net.trainParam.time;
doValidation = ~isempty(VV);
doTest = ~isempty(TV);

% Initialize
stop = '';
startTime = clock;
X = getx(net);
if (doValidation)
  VV.net = net;
  VV.perf = calcperf(net,X,VV.Pd,VV.Tl,VV.Ai,VV.Q,VV.TS);
  VV.numFail = 0;
end
tr = newtr(epochs,'perf','vperf','tperf');

% Train
for epoch=0:epochs

  % Performance and Gradient
  [perf,E,Ac,N,Zb,Zi,Zl] = calcperf(net,X,Pd,Tl,Ai,Q,TS);
  [gX,normgX] = calcgx(net,X,Pd,Zb,Zi,Zl,N,Ac,E,perf,Q,TS);
  
  % Training Record
  epochPlus1 = epoch+1;
  tr.perf(epochPlus1) = perf;
  if (doValidation)
    tr.vperf(epochPlus1) = VV.perf;
  end
  if (doTest)
    tr.tperf(epochPlus1) = calcperf(net,X,TV.Pd,TV.Tl,TV.Ai,TV.Q,TV.TS);
  end
  
  % Stopping Criteria
  currentTime = etime(clock,startTime);
  if (perf <= goal)
    stop = 'Performance goal met.';
  elseif (epoch == epochs)
    stop = 'Maximum epoch reached, performance goal was not met.';
  elseif (currentTime > time)
    stop = 'Maximum time elapsed, performance goal was not met.';
  elseif (normgX < min_grad)
    stop = 'Minimum gradient reached, performance goal was not met.';
  elseif (doValidation) & (VV.numFail > max_fail)
    stop = 'Validation stop.';
	net = VV.net;
  end
  
abc=0;
%abc=1;  
if abc==1

  % Progress
  if ~rem(epoch,show) | length(stop)
    fprintf(this);
	if isfinite(epochs) fprintf(', Epoch %g/%g',epoch, epochs); end
	if isfinite(time) fprintf(', Time %g%%',currentTime/time/100); end
	if isfinite(goal) fprintf(', %s %g/%g',upper(net.performFcn),perf,goal); end
   if isfinite(min_grad) fprintf(', Gradient %g/%g',normgX,min_grad); end
   fprintf(' Learn rate = %g',lr);
	fprintf('\n')
	plotperf(tr,goal,this,epoch)
    if length(stop) fprintf('%s, %s\n\n',this,stop); break; end
  end
end

  % Gradient Descent
  dX = lr*gX;  
  
%anet size 2-5-1 (21) cnet 3-5-5-1 (56) = 77 D. Liu 1/4/2001
%anet size 4-6-1 (37) cnet 5-6-1 (43) = 80 D. Liu, 1/6/2001
%dd=dX; dX=[]; dX=[dd(1:37); zeros(43,1)]; 
kanets=nia*nh1a+nh1a*noa+nh1a+noa;
kcnets=nic*nh1c+nh1c*1+nh1c+1;
dd=dX; dX=[]; dX=[dd(1:kanets); zeros(kcnets,1)];

X = X + dX;

% D. Liu 1/6/2001
%dn=X(1:24); 
%xnor=norm(dn,1); X=X/xnor; % norm less than 1
%mn=reshape(dn,6,4); xnor=norm(mn,1); mn=mn/xnor; xdn=reshape(mn,24,1);
%X=[xdn; X(25:80)];


  net = setx(net,X);
    
  % Validation
  if (doValidation)
    vperf = calcperf(net,X,VV.Pd,VV.Tl,VV.Ai,VV.Q,VV.TS);
	if (vperf < VV.perf)
	  VV.perf = vperf; VV.net = net; VV.numFail = 0;
	elseif (vperf > VV.perf)
      VV.numFail = VV.numFail + 1;
	end
  end
end

% Finish
tr = cliptr(tr,epoch);
