   "Protocol to return a function that computes the gradient of the loss function for a module."
   (loss-gradient-fn [m]
     "Gets a fn [output traget] that computes the loss gradient for a module. May return nil to
-     indicate that no loss function is specified."))
+  indicate that no loss function is specified."))
 
 (defprotocol PNeuralTraining
   "Protocol for modules that can be trained with forward / back propagation."
   (forward [this input]
     "Run a forward computation pass and return the updated module. output will be
-    available for later retrieval. Input and intermediate states will be stored for
-    futuere backward pass usage.")
+  available for later retrieval. Input and intermediate states will be stored for
+  futuere backward pass usage.")
 
   (backward [this input output-gradient]
     "Back propagate errors through the module with respect to the input.  Returns the
-    module with input-gradient set (gradient at the inputs). Input must be the same
-    as used in the forward pass.")
+  module with input-gradient set (gradient at the inputs). Input must be the same
+  as used in the forward pass.")
 
   (input-gradient [this]
     "Gets the computed input gradients for a module. Assumes the backward pass has been run."))


(defprotocol PGradient
  "Protocol for a module that supports accumulated gradients for optimisation. This vector should be exactly the
   same length as the parameter vector.
   The default implementation returns an empty gradient vector."
  (gradient [m]
    "Gets the accumulated gradient for this module, as a vector."))


  "Protocol for modules that can be trained input / output pairs."
  (train [this input output]
    "Trains the module to produce the given input / output. Accumulates gradients as necessary.
  Intended for use with update-parameters after completion of a (mini-)batch."))

(defprotocol PGradientOptimiser
  "A gradient optimiser is an abstraction for objects that update parameters based on gradient observations.
  Gradient optimisers typically contain relating to previous observations, momentum etc."
  (compute-parameters
    [optimiser gradient parameters]
    "Computes updated parameters using the given average gradient. Returns the updated gradient optimiser.
  Users can then call `parameters` on this object to get the updated parameters"))

(defprotocol PLossFunction
  "A function that calculates loss of a vector output vs. a target value"
  (loss [this v target]
    "Computes the loss for a value v against a target")
  (loss-gradient [this v target]
    "Computes the gradient of the loss with respect to v"))

(defprotocol PSerialize
  "Default implementation simply calls into {} and adds type.  An override
  is necessary if your class has complex object members instead of simple keys.
  The map must have a key named :record-type that is the fully namespaced
  qualified record class name (.getName (type item)).  The system will look
  for a function map->TypeName in order to create an empty object."
  (->map [this])
  (map-> [this map-data]))

