diff --git a/cloud-microphysics/app/train-cloud-microphysics.f90 b/cloud-microphysics/app/train-cloud-microphysics.f90
index 62b6b8701..0e82b37f2 100644
--- a/cloud-microphysics/app/train-cloud-microphysics.f90
+++ b/cloud-microphysics/app/train-cloud-microphysics.f90
@@ -13,10 +13,11 @@ program train_on_flat_distribution
   use julienne_m, only : string_t, file_t, command_line_t, bin_t
   use assert_m, only : assert, intrinsic_array_t
   use inference_engine_m, only : &
-    inference_engine_t, mini_batch_t, input_output_pair_t, tensor_t, trainable_engine_t, rkind, tensor_range_t, &
-    training_configuration_t, shuffle, phase_space_bin_t
+    inference_engine_t, mini_batch_t, input_output_pair_t, tensor_t, trainable_engine_t, rkind, tensor_map_t, &
+    training_configuration_t, shuffle
 
   !! Internal dependencies;
+  use phase_space_bin_m, only : phase_space_bin_t
   use NetCDF_file_m, only: NetCDF_file_t
   use ubounds_m, only : ubounds_t
   implicit none
@@ -279,12 +280,11 @@ subroutine read_train_write(training_configuration, base_name, plot_unit, previo
           ), lon = 1, size(qv_in,1))], lat = 1, size(qv_in,2))], level = 1, size(qv_in,3))], time = start_step, end_step, stride)]
 
         print *,"Calculating output tensor component ranges."
-        associate(output_range => tensor_range_t( &
-          layer  = "outputs", &
-          minima = [minval(dpt_dt), minval(dqv_dt), minval(dqc_dt), minval(dqr_dt), minval(dqs_dt)], &
-          maxima = [maxval(dpt_dt), maxval(dqv_dt), maxval(dqc_dt), maxval(dqr_dt), maxval(dqs_dt)], &
-          num_bins = num_bins &
-        ))
+        associate( &
+          output_minima => [minval(dpt_dt), minval(dqv_dt), minval(dqc_dt), minval(dqr_dt), minval(dqs_dt)], &
+          output_maxima => [maxval(dpt_dt), maxval(dqv_dt), maxval(dqc_dt), maxval(dqr_dt), maxval(dqs_dt)] &
+        )
+        associate( output_map => tensor_map_t(layer = "outputs", minima = output_minima, maxima = output_maxima))
           read_or_initialize_engine: &
           if (io_status==0) then
             print *,"Reading network from file " // network_file
@@ -301,29 +301,26 @@ subroutine read_train_write(training_configuration, base_name, plot_unit, previo
 
               print *,"Calculating input tensor component ranges."
               associate( &
-                input_range => tensor_range_t( &
+                input_map => tensor_map_t( &
                   layer  = "inputs", &
                   minima = [minval(pressure_in), minval(potential_temperature_in), minval(temperature_in), &
                     minval(qv_in), minval(qc_in), minval(qr_in), minval(qs_in)], &
                   maxima = [maxval(pressure_in), maxval(potential_temperature_in), maxval(temperature_in), &
-                    maxval(qv_in), maxval(qc_in), maxval(qr_in), maxval(qs_in)], &
-                  num_bins = num_bins &
-                ), &
-                date_string => string_t(date) &
-              )
+                    maxval(qv_in), maxval(qc_in), maxval(qr_in), maxval(qs_in)] &
+              ) ) 
                 associate(activation => training_configuration%differentiable_activation_strategy())
                   associate(residual_network => string_t(trim(merge("true ", "false", training_configuration%skip_connections())))) 
                     trainable_engine = trainable_engine_t( &
                       training_configuration,  &
                       perturbation_magnitude = 0.05, &
                       metadata = [ &
-                        string_t("Simple microphysics"), string_t("train-on-flat-dist"), date_string, activation%function_name(), &
+                        string_t("Simple microphysics"), string_t("train-on-flat-dist"), string_t(date), activation%function_name(), &
                         residual_network &
-                      ], input_range = input_range, output_range = output_range &
+                      ], input_map = input_map, output_map = output_map &
                     )
                   end associate
                 end associate
-              end associate ! input_range, date_string
+              end associate ! input_map, date_string
             end block initialize_network
           end if read_or_initialize_engine
 
@@ -336,7 +333,7 @@ subroutine read_train_write(training_configuration, base_name, plot_unit, previo
             occupied = .false.
             keepers = .false.
 
-            bin = [(output_range%bin(outputs(i), num_bins), i=1,size(outputs))]
+            bin = [(phase_space_bin_t(outputs(i), output_minima, output_maxima, num_bins), i=1,size(outputs))]
 
             do i = 1, size(outputs)
               if (occupied(bin(i)%loc(1),bin(i)%loc(2),bin(i)%loc(3),bin(i)%loc(4),bin(i)%loc(5))) cycle
@@ -347,7 +344,8 @@ subroutine read_train_write(training_configuration, base_name, plot_unit, previo
             print *, "Kept ", size(input_output_pairs), " out of ", size(outputs, kind=int64), " input/output pairs " // &
                      " in ", count(occupied)," out of ", size(occupied, kind=int64), " bins."
           end block
-        end associate ! output_range
+        end associate ! output_map
+        end associate 
 
         print *,"Normalizing the remaining input and output tensors"
         input_output_pairs = trainable_engine%map_to_training_ranges(input_output_pairs)
diff --git a/cloud-microphysics/src/phase_space_bin_m.f90 b/cloud-microphysics/src/phase_space_bin_m.f90
new file mode 100644
index 000000000..7ad1f0a61
--- /dev/null
+++ b/cloud-microphysics/src/phase_space_bin_m.f90
@@ -0,0 +1,39 @@
+module phase_space_bin_m
+  use kind_parameters_m, only : rkind
+  use tensor_map_m, only : tensor_map_t
+  use tensor_m, only : tensor_t
+  implicit none
+  
+  public :: phase_space_bin_t
+
+  type phase_space_bin_t
+    integer, allocatable :: loc(:)
+  end type
+ 
+  interface phase_space_bin_t
+
+    pure module function bin(tensor, minima, maxima, num_bins) result(phase_space_bin)
+      implicit none
+      type(tensor_t), intent(in) :: tensor
+      real(rkind), intent(in) :: minima(:), maxima(:)
+      integer, intent(in) :: num_bins
+      type(phase_space_bin_t) phase_space_bin
+    end function
+
+  end interface
+
+contains
+
+  module procedure bin
+
+    real(rkind), parameter :: half = 0.5_rkind
+
+    associate(bin_widths => (maxima - minima)/real(num_bins,rkind))
+      associate(tensor_values => min(tensor%values(), maxima - half*bin_widths))
+        phase_space_bin%loc = (tensor_values - minima)/bin_widths + 1
+      end associate
+    end associate
+
+  end procedure
+
+end module phase_space_bin_m
diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90
index 3a311b3de..ef7fe1ade 100644
--- a/src/inference_engine/inference_engine_m_.f90
+++ b/src/inference_engine/inference_engine_m_.f90
@@ -8,7 +8,7 @@ module inference_engine_m_
   use kind_parameters_m, only : rkind
   use metadata_m, only : metadata_t
   use tensor_m, only : tensor_t
-  use tensor_range_m, only : tensor_range_t
+  use tensor_map_m, only : tensor_map_t
   implicit none
 
   private
@@ -20,7 +20,7 @@ module inference_engine_m_
   type inference_engine_t
     !! Encapsulate the minimal information needed to perform inference
     private
-    type(tensor_range_t) input_range_, output_range_
+    type(tensor_map_t) input_map_, output_map_
     type(metadata_t) metadata_
     real(rkind), allocatable :: weights_(:,:,:), biases_(:,:)
     integer, allocatable :: nodes_(:)
@@ -43,7 +43,7 @@ module inference_engine_m_
   end type
 
   type exchange_t
-    type(tensor_range_t) input_range_, output_range_
+    type(tensor_map_t) input_map_, output_map_
     type(metadata_t) metadata_
     real(rkind), allocatable :: weights_(:,:,:), biases_(:,:)
     integer, allocatable :: nodes_(:)
@@ -60,13 +60,13 @@ module inference_engine_m_
 
   interface inference_engine_t
 
-    impure module function construct_from_padded_arrays(metadata, weights, biases, nodes, input_range, output_range) &
+    impure module function construct_from_padded_arrays(metadata, weights, biases, nodes, input_map, output_map) &
       result(inference_engine)
       implicit none
       type(string_t), intent(in) :: metadata(:)
       real(rkind), intent(in) :: weights(:,:,:), biases(:,:)
       integer, intent(in) :: nodes(0:)
-      type(tensor_range_t), intent(in), optional :: input_range, output_range
+      type(tensor_map_t), intent(in), optional :: input_map, output_map
       type(inference_engine_t) inference_engine
     end function
 
diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90
index 1bda5d376..5f3696047 100644
--- a/src/inference_engine/inference_engine_s.F90
+++ b/src/inference_engine/inference_engine_s.F90
@@ -21,16 +21,16 @@
 contains
 
   module procedure map_to_input_range
-    normalized_tensor = self%input_range_%map_to_training_range(tensor)
+    normalized_tensor = self%input_map_%map_to_training_range(tensor)
   end procedure
 
   module procedure map_from_output_range
-    tensor = self%output_range_%map_from_training_range(normalized_tensor)
+    tensor = self%output_map_%map_from_training_range(normalized_tensor)
   end procedure
 
   module procedure to_exchange
-    exchange%input_range_ = self%input_range_
-    exchange%output_range_ = self%output_range_
+    exchange%input_map_ = self%input_map_
+    exchange%output_map_ = self%output_map_
     associate(strings => self%metadata_%strings())
       exchange%metadata_ = metadata_t(strings(1),strings(2),strings(3),strings(4),strings(5))
     end associate
@@ -53,13 +53,13 @@
       allocate(a(maxval(n), input_layer:output_layer))
 
 #ifndef _CRAYFTN
-      associate(normalized_inputs => self%input_range_%map_to_training_range(inputs))
+      associate(normalized_inputs => self%input_map_%map_to_training_range(inputs))
         a(1:n(input_layer),input_layer) = normalized_inputs%values()
       end associate
 #else
       block
         type(tensor_t) normalized_inputs
-        normalized_inputs = self%input_range_%map_to_training_range(inputs)
+        normalized_inputs = self%input_map_%map_to_training_range(inputs)
         a(1:n(input_layer),input_layer) = normalized_inputs%values()
       end block
 #endif
@@ -78,7 +78,7 @@
 #else
       associate(normalized_outputs => tensor_t(a(1:n(output_layer), output_layer)))
 #endif
-        outputs = self%output_range_%map_from_training_range(normalized_outputs)
+        outputs = self%output_map_%map_from_training_range(normalized_outputs)
 #ifdef _CRAYFTN
       end block
 #else
@@ -162,22 +162,22 @@ impure function activation_factory_method(activation_name) result(activation)
     block
       integer i
 
-      if (present(input_range)) then
-        inference_engine%input_range_ = input_range
+      if (present(input_map)) then
+        inference_engine%input_map_ = input_map
       else
         associate(num_inputs => nodes(lbound(nodes,1)))
           associate(default_minima => [(0., i=1,num_inputs)], default_maxima => [(1., i=1,num_inputs)])
-            inference_engine%input_range_ = tensor_range_t("inputs", default_minima, default_maxima)
+            inference_engine%input_map_ = tensor_map_t("inputs", default_minima, default_maxima)
           end associate
         end associate
       end if
 
-      if (present(output_range)) then
-        inference_engine%output_range_ = output_range
+      if (present(output_map)) then
+        inference_engine%output_map_ = output_map
       else
         associate(num_outputs => nodes(ubound(nodes,1)))
           associate(default_minima => [(0., i=1,num_outputs)], default_maxima => [(1., i=1,num_outputs)])
-            inference_engine%output_range_ = tensor_range_t("outputs", default_minima, default_maxima)
+            inference_engine%output_map_ = tensor_map_t("outputs", default_minima, default_maxima)
           end associate
         end associate
       end if
@@ -193,15 +193,15 @@ impure function activation_factory_method(activation_name) result(activation)
   module procedure from_json
 
     type(string_t), allocatable :: lines(:)
-    type(tensor_range_t) input_range, output_range
+    type(tensor_map_t) input_map, output_map
     type(layer_t) hidden_layers, output_layer
     character(len=:), allocatable :: justified_line
     integer l
 #ifdef _CRAYFTN
-    type(tensor_range_t) proto_range
+    type(tensor_map_t) proto_map
     type(metadata_t) proto_meta
     type(neuron_t) proto_neuron
-    proto_range = tensor_range_t("",[0.],[1.])
+    proto_map = tensor_map_t("",[0.],[1.])
     proto_meta = metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t(""))
     proto_neuron = neuron_t(weights=[0.], bias=0.)
 #endif
@@ -213,25 +213,25 @@ impure function activation_factory_method(activation_name) result(activation)
     associate(num_lines => size(lines))
       
 #ifndef _CRAYFTN
-      associate(proto_range => tensor_range_t("",[0.],[1.]))
+      associate(proto_map => tensor_map_t("",[0.],[1.]))
 #endif
-        associate(range_lines => size(proto_range%to_json()))
+        associate(map_lines => size(proto_map%to_json()))
 
-          find_inputs_range: &
+          find_inputs_map: &
           do l = 1, num_lines
             justified_line = adjustl(lines(l)%string())
-            if (justified_line == '"inputs_range": {') exit 
-          end do find_inputs_range
-          call assert(justified_line =='"inputs_range": {', 'from_json: expecting "inputs_range": {', justified_line)
-          input_range = tensor_range_t(lines(l:l+range_lines-1))
+            if (justified_line == '"inputs_map": {') exit
+          end do find_inputs_map
+          call assert(justified_line =='"inputs_map": {', 'from_json: expecting "inputs_map": {', justified_line)
+          input_map = tensor_map_t(lines(l:l+map_lines-1))
 
-          find_outputs_range: &
+          find_outputs_map: &
           do l = 1, num_lines
             justified_line = adjustl(lines(l)%string())
-            if (justified_line == '"outputs_range": {') exit 
-          end do find_outputs_range
-          call assert(justified_line =='"outputs_range": {', 'from_json: expecting "outputs_range": {', justified_line)
-          output_range = tensor_range_t(lines(l:l+range_lines-1))
+            if (justified_line == '"outputs_map": {') exit
+          end do find_outputs_map
+          call assert(justified_line =='"outputs_map": {', 'from_json: expecting "outputs_map": {', justified_line)
+          output_map = tensor_map_t(lines(l:l+map_lines-1))
 
         end associate
 #ifndef _CRAYFTN
@@ -283,7 +283,7 @@ impure function activation_factory_method(activation_name) result(activation)
 #endif
        associate(metadata => metadata_t(lines(l:l+size(proto_meta%to_json())-1)))
          associate(metadata_strings => metadata%strings())
-           inference_engine = hidden_layers%inference_engine(metadata_strings, output_layer, input_range, output_range)
+           inference_engine = hidden_layers%inference_engine(metadata_strings, output_layer, input_map, output_map)
            if (allocated(inference_engine%activation_strategy_)) deallocate(inference_engine%activation_strategy_)
            allocate(inference_engine%activation_strategy_, source = activation_factory_method(metadata_strings(4)%string()))
          end associate
@@ -433,10 +433,10 @@ function get_key_value(line) result(value_)
   module procedure to_json
 
 #ifdef _CRAYFTN
-    type(tensor_range_t) proto_range
+    type(tensor_map_t) proto_map
     type(metadata_t) proto_meta
     type(neuron_t) proto_neuron
-    proto_range = tensor_range_t("",[zero],[one])
+    proto_map = tensor_map_t("",[zero],[one])
     proto_meta = metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t(""))
     proto_neuron = neuron_t([zero],one)
 #endif
@@ -450,14 +450,14 @@ function get_key_value(line) result(value_)
       ,first_hidden => lbound(self%nodes_,1) + 1 &
       ,last_hidden => ubound(self%nodes_,1) - 1 &
 #ifndef _CRAYFTN
-      ,proto_range => tensor_range_t("",[zero],[one]) &
+      ,proto_map => tensor_map_t("",[zero],[one]) &
       ,proto_meta => metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t("")) &
       ,proto_neuron => neuron_t([zero],zero) &
 #endif
     )
       associate( &
         metadata_lines => size(proto_meta%to_json()), &
-        tensor_range_lines => size(proto_range%to_json()), &
+        tensor_map_lines => size(proto_map%to_json()), &
         neuron_lines => size(proto_neuron%to_json()) &
       )
         block
@@ -468,8 +468,8 @@ function get_key_value(line) result(value_)
           associate( json_lines => &
             brace + &                                                          ! { 
               metadata_lines + &                                               !   "metadata": ...
-              tensor_range_lines + &                                           !   "inputs_tensor_range": ...
-              tensor_range_lines + &                                           !   "outputs_tensor_range": ...
+              tensor_map_lines + &                                           !   "inputs_tensor_map": ...
+              tensor_map_lines + &                                           !   "outputs_tensor_map": ...
                 bracket_hidden_layers_array + &                                !   "hidden_layers": [
                   bracket_layer*num_hidden_layers + &                          !      [
                     neuron_lines*sum(self%nodes_(first_hidden:last_hidden))+ & !        neuron ...
@@ -485,14 +485,14 @@ function get_key_value(line) result(value_)
             associate(meta_start => brace + 1,  meta_end => brace + metadata_lines)
               lines(meta_start:meta_end) = self%metadata_%to_json()
               lines(meta_end) = lines(meta_end) // ","
-              associate(input_range_start => meta_end + 1,  input_range_end => meta_end + tensor_range_lines)
-                lines(input_range_start:input_range_end) =  self%input_range_%to_json()
-                lines(input_range_end) = lines(input_range_end) // ","
-                associate(output_range_start => input_range_end + 1,  output_range_end => input_range_end + tensor_range_lines)
-                  lines(output_range_start:output_range_end) =  self%output_range_%to_json()
-                  lines(output_range_end) = lines(output_range_end) // ","
-                  lines(output_range_end + 1) = string_t('     "hidden_layers": [')
-                  line= output_range_end + 1
+              associate(input_map_start => meta_end + 1,  input_map_end => meta_end + tensor_map_lines)
+                lines(input_map_start:input_map_end) =  self%input_map_%to_json()
+                lines(input_map_end) = lines(input_map_end) // ","
+                associate(output_map_start => input_map_end + 1,  output_map_end => input_map_end + tensor_map_lines)
+                  lines(output_map_start:output_map_end) =  self%output_map_%to_json()
+                  lines(output_map_end) = lines(output_map_end) // ","
+                  lines(output_map_end + 1) = string_t('     "hidden_layers": [')
+                  line= output_map_end + 1
                 end associate
               end associate
             end associate
diff --git a/src/inference_engine/layer_m.f90 b/src/inference_engine/layer_m.f90
index 299736e0e..78fccbd89 100644
--- a/src/inference_engine/layer_m.f90
+++ b/src/inference_engine/layer_m.f90
@@ -4,7 +4,7 @@ module layer_m
   use neuron_m, only : neuron_t
   use julienne_string_m, only : string_t
   use inference_engine_m_, only : inference_engine_t
-  use tensor_range_m, only :  tensor_range_t
+  use tensor_map_m, only :  tensor_map_t
   implicit none
 
   private
@@ -39,12 +39,12 @@ recursive module function construct_layer(layer_lines, start) result(layer)
 
   interface
 
-    module function inference_engine(hidden_layers, metadata, output_layer, input_range, output_range) result(inference_engine_)
+    module function inference_engine(hidden_layers, metadata, output_layer, input_map, output_map) result(inference_engine_)
       implicit none
       class(layer_t), intent(in), target :: hidden_layers
       type(layer_t), intent(in), target :: output_layer
       type(string_t), intent(in) :: metadata(:)
-      type(tensor_range_t), intent(in) :: input_range, output_range
+      type(tensor_map_t), intent(in) :: input_map, output_map
       type(inference_engine_t) inference_engine_
     end function
 
diff --git a/src/inference_engine/layer_s.f90 b/src/inference_engine/layer_s.f90
index 345af9b01..1dc847d74 100644
--- a/src/inference_engine/layer_s.f90
+++ b/src/inference_engine/layer_s.f90
@@ -102,7 +102,7 @@
 
             end do loop_over_output_neurons
 
-            inference_engine_ = inference_engine_t(metadata, weights, biases, nodes, input_range, output_range)
+            inference_engine_ = inference_engine_t(metadata, weights, biases, nodes, input_map, output_map)
           end block
         end associate
       end associate
diff --git a/src/inference_engine/tensor_map_m.f90 b/src/inference_engine/tensor_map_m.f90
new file mode 100644
index 000000000..5560cf168
--- /dev/null
+++ b/src/inference_engine/tensor_map_m.f90
@@ -0,0 +1,72 @@
+! Copyright (c), The Regents of the University of California
+! Terms of use are as specified in LICENSE.txt
+module tensor_map_m
+  use tensor_m, only : tensor_t
+  use julienne_m, only : string_t
+  use kind_parameters_m, only : rkind
+  implicit none
+  
+  private
+  public :: tensor_map_t
+
+  type tensor_map_t
+    private
+    character(len=:), allocatable :: layer_
+    real(rkind), allocatable, dimension(:) :: intercept_, slope_
+  contains
+    procedure map_to_training_range
+    procedure map_from_training_range
+    procedure to_json
+    generic :: operator(==) => equals
+    procedure, private :: equals
+  end type
+
+
+  interface tensor_map_t
+
+    pure module function from_component_ranges(layer, minima, maxima) result(tensor_map)
+      implicit none
+      character(len=*), intent(in) :: layer
+      real(rkind), dimension(:), intent(in) :: minima, maxima
+      type(tensor_map_t) tensor_map
+    end function
+
+    module function from_json(lines) result(tensor_map)
+      implicit none
+      type(string_t), intent(in) :: lines(:)
+      type(tensor_map_t) tensor_map
+    end function
+
+  end interface
+
+  interface
+
+    elemental module function map_to_training_range(self, tensor) result(normalized_tensor)
+      implicit none
+      class(tensor_map_t), intent(in) :: self
+      type(tensor_t), intent(in) :: tensor
+      type(tensor_t) normalized_tensor
+    end function
+
+    elemental module function map_from_training_range(self, tensor) result(unnormalized_tensor)
+      implicit none
+      class(tensor_map_t), intent(in) :: self
+      type(tensor_t), intent(in) :: tensor
+      type(tensor_t) unnormalized_tensor
+    end function
+
+    pure module function to_json(self) result(lines)
+      implicit none
+      class(tensor_map_t), intent(in) :: self
+      type(string_t), allocatable :: lines(:)
+    end function
+
+    elemental module function equals(lhs, rhs) result(lhs_equals_rhs)
+      implicit none
+      class(tensor_map_t), intent(in) :: lhs, rhs
+      logical lhs_equals_rhs
+    end function
+
+  end interface
+
+end module tensor_map_m
diff --git a/src/inference_engine/tensor_map_s.f90 b/src/inference_engine/tensor_map_s.f90
new file mode 100644
index 000000000..dd52ab364
--- /dev/null
+++ b/src/inference_engine/tensor_map_s.f90
@@ -0,0 +1,94 @@
+! Copyright (c), The Regents of the University of California
+! Terms of use are as specified in LICENSE.txt
+submodule(tensor_map_m) tensor_map_s
+  use assert_m, only : assert
+  use julienne_m, only : separated_values
+  use kind_parameters_m, only : rkind
+  implicit none
+  
+contains
+
+  module procedure from_component_ranges
+    call assert(size(minima)==size(maxima),"tensor_map_s(from_components): size(minima)==size(maxima)")
+    tensor_map%layer_ = layer
+    tensor_map%intercept_ = minima
+    tensor_map%slope_ = maxima - minima
+  end procedure
+
+  module procedure from_json
+    logical tensor_map_key_found
+    integer l 
+
+    tensor_map_key_found = .false.
+
+    do l=1,size(lines)
+      if (lines(l)%get_json_key() == "inputs_map" .or. lines(l)%get_json_key() == "outputs_map") then
+        tensor_map_key_found = .true.
+        tensor_map%layer_  = lines(l+1)%get_json_value(key=string_t("layer"), mold=string_t(""))
+        tensor_map%intercept_ = lines(l+2)%get_json_value(key=string_t("intercept"), mold=[0.])
+        tensor_map%slope_ = lines(l+3)%get_json_value(key=string_t("slope"), mold=[0.])
+        return
+      end if
+    end do 
+
+    call assert(tensor_map_key_found, "tensor_map_s(from_json): 'tensor_map' key found")
+  end procedure
+
+  module procedure equals
+    real, parameter :: tolerance = 1.E-08
+
+    call assert(allocated(lhs%layer_) .and. allocated(rhs%layer_), "tensor_map_s(equals): allocated layer_ components")
+    call assert(allocated(lhs%intercept_) .and. allocated(rhs%intercept_), "tensor_map_s(equals): allocated intercept_ components)")
+    call assert(allocated(lhs%slope_) .and.  allocated(rhs%slope_), "tensor_map_s(equals): allocated slope_ components)")
+    call assert(size(lhs%intercept_) == size(rhs%intercept_), "tensor_map_s(equals): size(lhs%intercept_) == size(rhs%intercept_)")
+    call assert(size(lhs%slope_) == size(rhs%slope_), "tensor_map_s(equals): size(lhs%slope_) == size(rhs%slope_)")
+
+    lhs_equals_rhs = &
+      lhs%layer_ == rhs%layer_ .and. &
+      all(abs(lhs%intercept_ - rhs%intercept_) <= tolerance).and. &
+      all(abs(lhs%slope_ - rhs%slope_) <= tolerance)
+  end procedure 
+
+  module procedure to_json
+    integer, parameter :: characters_per_value=17
+    character(len=*), parameter :: indent = repeat(" ",ncopies=4)
+    character(len=:), allocatable :: csv_format, intercept_string, slope_string
+
+    call assert(allocated(self%layer_), "tensor_map_s(to_json): allocated layer_")
+    call assert(allocated(self%intercept_) .and. allocated(self%slope_), "tensor_map_s(to_json): allocated intercept_/slope_")
+
+    csv_format = separated_values(separator=",", mold=[real(rkind)::])
+    allocate(character(len=size(self%intercept_)*(characters_per_value+1)-1)::intercept_string)
+    allocate(character(len=size(self%slope_)*(characters_per_value+1)-1)::slope_string)
+    write(intercept_string, fmt = csv_format) self%intercept_
+    write(slope_string, fmt = csv_format) self%slope_
+    block 
+      character(len=:), allocatable :: layer
+      layer = trim(adjustl(self%layer_))
+      lines = [ &
+        string_t(indent // '"'//layer//'_map": {'), &
+        string_t(indent // '  "layer": "' // layer // '",'), &
+        string_t(indent // '  "intercept": [' // trim(adjustl(intercept_string)) // '],'), & 
+        string_t(indent // '  "slope": [' // trim(adjustl(slope_string)) // ']'), &
+        string_t(indent // '}') &
+      ]
+    end block
+  end procedure
+
+  module procedure map_to_training_range
+    associate(tensor_values => tensor%values())
+      associate(normalized_values => (tensor_values - self%intercept_)/self%slope_)
+        normalized_tensor = tensor_t(normalized_values)
+      end associate
+    end associate
+  end procedure
+
+  module procedure map_from_training_range
+    associate(tensor_values => tensor%values())
+      associate(unnormalized_values => self%intercept_ + tensor_values*self%slope_)
+        unnormalized_tensor = tensor_t(unnormalized_values)
+      end associate
+    end associate
+  end procedure
+
+end submodule tensor_map_s
diff --git a/src/inference_engine/tensor_range_m.f90 b/src/inference_engine/tensor_range_m.f90
deleted file mode 100644
index 818f36a6a..000000000
--- a/src/inference_engine/tensor_range_m.f90
+++ /dev/null
@@ -1,94 +0,0 @@
-! Copyright (c), The Regents of the University of California
-! Terms of use are as specified in LICENSE.txt
-module tensor_range_m
-  use tensor_m, only : tensor_t
-  use julienne_m, only : string_t
-  implicit none
-  
-  private
-  public :: tensor_range_t
-  public :: phase_space_bin_t
-
-  type phase_space_bin_t
-    integer, allocatable :: loc(:)
-  end type
-
-  type tensor_range_t
-    private
-    character(len=:), allocatable :: layer_
-    real, allocatable, dimension(:) :: minima_, maxima_, bin_widths_
-  contains
-    procedure map_to_training_range
-    procedure map_from_training_range
-    procedure to_json
-    procedure bin
-    procedure in_range
-    generic :: operator(==) => equals
-    procedure, private :: equals
-  end type
-
-
-  interface tensor_range_t
-
-    pure module function from_components(layer, minima, maxima, num_bins) result(tensor_range)
-      implicit none
-      character(len=*), intent(in) :: layer
-      real, dimension(:), intent(in) :: minima, maxima
-      integer, intent(in), optional :: num_bins
-      type(tensor_range_t) tensor_range
-    end function
-
-    module function from_json(lines) result(tensor_range)
-      implicit none
-      type(string_t), intent(in) :: lines(:)
-      type(tensor_range_t) tensor_range
-    end function
-
-  end interface
-
-  interface
-
-    elemental module function map_to_training_range(self, tensor) result(normalized_tensor)
-      implicit none
-      class(tensor_range_t), intent(in) :: self
-      type(tensor_t), intent(in) :: tensor
-      type(tensor_t) normalized_tensor
-    end function
-
-    elemental module function map_from_training_range(self, tensor) result(unnormalized_tensor)
-      implicit none
-      class(tensor_range_t), intent(in) :: self
-      type(tensor_t), intent(in) :: tensor
-      type(tensor_t) unnormalized_tensor
-    end function
-
-    pure module function to_json(self) result(lines)
-      implicit none
-      class(tensor_range_t), intent(in) :: self
-      type(string_t), allocatable :: lines(:)
-    end function
-
-    elemental module function equals(lhs, rhs) result(lhs_equals_rhs)
-      implicit none
-      class(tensor_range_t), intent(in) :: lhs, rhs
-      logical lhs_equals_rhs
-    end function
-
-    pure module function bin(self, tensor, num_bins) result(phase_space_bin)
-      implicit none
-      class(tensor_range_t), intent(in) :: self
-      type(tensor_t), intent(in) :: tensor
-      integer, intent(in) :: num_bins
-      type(phase_space_bin_t) phase_space_bin
-    end function
-
-    elemental module function in_range(self, tensor) result(is_in_range)
-      implicit none
-      class(tensor_range_t), intent(in) :: self
-      type(tensor_t), intent(in) :: tensor
-      logical is_in_range
-    end function
-
-  end interface
-
-end module tensor_range_m
diff --git a/src/inference_engine/tensor_range_s.f90 b/src/inference_engine/tensor_range_s.f90
deleted file mode 100644
index 87ca5911e..000000000
--- a/src/inference_engine/tensor_range_s.f90
+++ /dev/null
@@ -1,112 +0,0 @@
-! Copyright (c), The Regents of the University of California
-! Terms of use are as specified in LICENSE.txt
-submodule(tensor_range_m) tensor_range_s
-  use assert_m, only : assert
-  use julienne_m, only : separated_values
-  use kind_parameters_m, only : rkind
-  implicit none
-  
-contains
-
-  module procedure from_components
-    call assert(size(minima)==size(maxima),"tensor_range_s(from_components): size(minima)==size(maxima)")
-    tensor_range%layer_ = layer
-    tensor_range%minima_ = minima
-    tensor_range%maxima_ = maxima 
-    if (present(num_bins)) then
-      tensor_range%bin_widths_ = (maxima - minima)/real(num_bins,rkind)
-    else
-      tensor_range%bin_widths_ = maxima - minima
-    end if
-  end procedure
-
-  module procedure from_json
-    logical tensor_range_key_found
-    integer l 
-
-    tensor_range_key_found = .false.
-
-    do l=1,size(lines)
-      if (lines(l)%get_json_key() == "inputs_range" .or. lines(l)%get_json_key() == "outputs_range") then
-        tensor_range_key_found = .true.
-        tensor_range%layer_  = lines(l+1)%get_json_value(key=string_t("layer"), mold=string_t(""))
-        tensor_range%minima_ = lines(l+2)%get_json_value(key=string_t("minima"), mold=[0.])
-        tensor_range%maxima_ = lines(l+3)%get_json_value(key=string_t("maxima"), mold=[0.])
-        return
-      end if
-    end do 
-
-    tensor_range%bin_widths_ = tensor_range%maxima_ - tensor_range%minima_
-
-    call assert(tensor_range_key_found, "tensor_range_s(from_json): 'tensor_range' key found")
-  end procedure
-
-  module procedure equals
-    real, parameter :: tolerance = 1.E-08
-
-    call assert(allocated(lhs%layer_) .and. allocated(rhs%layer_), "tensor_range_s(equals): allocated layer_ components")
-    call assert(allocated(lhs%minima_) .and. allocated(rhs%minima_), "tensor_range_s(equals): allocated minima_ components)")
-    call assert(allocated(lhs%maxima_) .and.  allocated(rhs%maxima_), "tensor_range_s(equals): allocated maxima_ components)")
-    call assert(size(lhs%minima_) == size(rhs%minima_), "tensor_range_s(equals): size(lhs%minima_) == size(rhs%minima_)")
-    call assert(size(lhs%maxima_) == size(rhs%maxima_), "tensor_range_s(equals): size(lhs%maxima_) == size(rhs%maxima_)")
-
-    lhs_equals_rhs = &
-      lhs%layer_ == rhs%layer_ .and. &
-      all(abs(lhs%minima_ - rhs%minima_) <= tolerance).and. &
-      all(abs(lhs%maxima_ - rhs%maxima_) <= tolerance)
-  end procedure 
-
-  module procedure to_json
-    integer, parameter :: characters_per_value=17
-    character(len=*), parameter :: indent = repeat(" ",ncopies=4)
-    character(len=:), allocatable :: csv_format, minima_string, maxima_string
-
-    call assert(allocated(self%layer_), "tensor_range_s(to_json): allocated layer_")
-    call assert(allocated(self%minima_) .and. allocated(self%maxima_), "tensor_range_s(to_json): allocated minima_/maxima_")
-
-    csv_format = separated_values(separator=",", mold=[real(rkind)::])
-    allocate(character(len=size(self%minima_)*(characters_per_value+1)-1)::minima_string)
-    allocate(character(len=size(self%maxima_)*(characters_per_value+1)-1)::maxima_string)
-    write(minima_string, fmt = csv_format) self%minima_
-    write(maxima_string, fmt = csv_format) self%maxima_
-    block 
-      character(len=:), allocatable :: layer
-      layer = trim(adjustl(self%layer_))
-      lines = [ &
-        string_t(indent // '"'//layer//'_range": {'), &
-        string_t(indent // '  "layer": "' // layer // '",'), &
-        string_t(indent // '  "minima": [' // trim(adjustl(minima_string)) // '],'), & 
-        string_t(indent // '  "maxima": [' // trim(adjustl(maxima_string)) // ']'), &
-        string_t(indent // '}') &
-      ]
-    end block
-  end procedure
-
-  module procedure map_to_training_range
-    associate(tensor_values => tensor%values())
-      associate(normalized_values => (tensor_values - self%minima_)/(self%maxima_ - self%minima_))
-        normalized_tensor = tensor_t(normalized_values)
-      end associate
-    end associate
-  end procedure
-
-  module procedure map_from_training_range
-    associate(tensor_values => tensor%values())
-      associate(unnormalized_values => self%minima_ + tensor_values*(self%maxima_ - self%minima_))
-        unnormalized_tensor = tensor_t(unnormalized_values)
-      end associate
-    end associate
-  end procedure
-
-  module procedure bin
-    real(rkind), parameter :: half = 0.5_rkind
-    associate(tensor_values => min(tensor%values(), self%maxima_ - half*self%bin_widths_))
-      phase_space_bin%loc = (tensor_values - self%minima_)/self%bin_widths_ + 1
-    end associate
-  end procedure
-
-  module procedure in_range
-    is_in_range = all(tensor%values() >= self%minima_) .and. all(tensor%values() <= self%maxima_)
-  end procedure
-
-end submodule tensor_range_s
diff --git a/src/inference_engine/trainable_engine_m.F90 b/src/inference_engine/trainable_engine_m.F90
index 8565fe8ae..74ffb04b1 100644
--- a/src/inference_engine/trainable_engine_m.F90
+++ b/src/inference_engine/trainable_engine_m.F90
@@ -8,7 +8,7 @@ module trainable_engine_m
   use kind_parameters_m, only : rkind
   use metadata_m, only : metadata_t
   use tensor_m, only :  tensor_t
-  use tensor_range_m, only :  tensor_range_t
+  use tensor_map_m, only :  tensor_map_t
   use mini_batch_m, only : mini_batch_t
   use training_configuration_m, only : training_configuration_t
   use input_output_pair_m, only : input_output_pair_t
@@ -20,7 +20,7 @@ module trainable_engine_m
   type trainable_engine_t
     !! Encapsulate the information needed to perform training
     private
-    type(tensor_range_t) input_range_, output_range_
+    type(tensor_map_t) input_map_, output_map_
     type(metadata_t) metadata_
     real(rkind), allocatable :: w(:,:,:) ! weights
     real(rkind), allocatable :: b(:,:) ! biases
@@ -49,11 +49,11 @@ module trainable_engine_m
   interface trainable_engine_t
 #ifdef __INTEL_COMPILER
      impure module function construct_trainable_engine_from_padded_arrays( &
-       nodes, weights, biases, differentiable_activation_strategy, metadata, input_range, output_range &
+       nodes, weights, biases, differentiable_activation_strategy, metadata, input_map, output_map &
      ) &
 #else
      impure module function construct_from_padded_arrays( &
-       nodes, weights, biases, differentiable_activation_strategy, metadata, input_range, output_range &
+       nodes, weights, biases, differentiable_activation_strategy, metadata, input_map, output_map &
      ) &
 #endif
       result(trainable_engine)
@@ -62,7 +62,7 @@ impure module function construct_from_padded_arrays( &
       real(rkind), intent(in)  :: weights(:,:,:), biases(:,:)
       class(differentiable_activation_strategy_t), intent(in) :: differentiable_activation_strategy
       type(string_t), intent(in) :: metadata(:)
-      type(tensor_range_t), intent(in), optional :: input_range, output_range
+      type(tensor_map_t), intent(in), optional :: input_map, output_map
       type(trainable_engine_t) trainable_engine
     end function
 
@@ -72,13 +72,13 @@ impure module function construct_from_inference_engine(inference_engine) result(
       type(trainable_engine_t) trainable_engine
     end function
 
-    module function perturbed_identity_network(training_configuration, perturbation_magnitude, metadata, input_range, output_range)&
+    module function perturbed_identity_network(training_configuration, perturbation_magnitude, metadata, input_map, output_map)&
       result(trainable_engine)
       implicit none
       type(training_configuration_t), intent(in) :: training_configuration
       type(string_t), intent(in) :: metadata(:)
       real(rkind), intent(in) :: perturbation_magnitude
-      type(tensor_range_t) input_range, output_range
+      type(tensor_map_t) input_map, output_map
       type(trainable_engine_t) trainable_engine
     end function
 
diff --git a/src/inference_engine/trainable_engine_s.F90 b/src/inference_engine/trainable_engine_s.F90
index afdf78d7a..45affdca8 100644
--- a/src/inference_engine/trainable_engine_s.F90
+++ b/src/inference_engine/trainable_engine_s.F90
@@ -35,8 +35,8 @@
     type(exchange_t) exchange
     exchange = inference_engine%to_exchange()
 #endif
-      trainable_engine%input_range_ = exchange%input_range_
-      trainable_engine%output_range_ = exchange%output_range_
+      trainable_engine%input_map_ = exchange%input_map_
+      trainable_engine%output_map_ = exchange%output_map_
       trainable_engine%metadata_ = exchange%metadata_
       trainable_engine%w = exchange%weights_
       trainable_engine%b = exchange%biases_
@@ -83,11 +83,11 @@
       allocate(a(maxval(n), input_layer:output_layer)) ! Activations
 
 #ifndef _CRAYFTN
-      associate(normalized_inputs => self%input_range_%map_to_training_range(inputs))
+      associate(normalized_inputs => self%input_map_%map_to_training_range(inputs))
 #else
       block
         type(tensor_t) normalized_inputs
-        normalized_inputs = self%input_range_%map_to_training_range(inputs)
+        normalized_inputs = self%input_map_%map_to_training_range(inputs)
 #endif
         a(1:n(input_layer),input_layer) = normalized_inputs%values()
 #ifndef _CRAYFTN
@@ -104,7 +104,7 @@
       end do feed_forward
  
       associate(normalized_outputs => tensor_t(a(1:n(output_layer), output_layer)))
-        outputs = self%output_range_%map_from_training_range(normalized_outputs)
+        outputs = self%output_map_%map_from_training_range(normalized_outputs)
       end associate
 
     end associate
@@ -271,19 +271,19 @@
     block 
       integer i
 
-      if (present(input_range)) then
-         trainable_engine%input_range_ = input_range
+      if (present(input_map)) then
+         trainable_engine%input_map_ = input_map
       else
         associate(num_inputs => nodes(lbound(nodes,1)))
-          trainable_engine%input_range_ = tensor_range_t("inputs", minima=[(0., i=1,num_inputs)], maxima=[(1., i=1,num_inputs)])
+          trainable_engine%input_map_ = tensor_map_t("inputs", minima=[(0., i=1,num_inputs)], maxima=[(1., i=1,num_inputs)])
         end associate
       end if
 
-      if (present(output_range)) then
-         trainable_engine%output_range_ = output_range
+      if (present(output_map)) then
+         trainable_engine%output_map_ = output_map
       else
         associate(num_outputs => nodes(ubound(nodes,1)))
-          trainable_engine%output_range_ = tensor_range_t("outputs", minima=[(0., i=1,num_outputs)], maxima=[(1., i=1,num_outputs)])
+          trainable_engine%output_map_ = tensor_map_t("outputs", minima=[(0., i=1,num_outputs)], maxima=[(1., i=1,num_outputs)])
         end associate
       end if
     end block
@@ -292,7 +292,7 @@
   end procedure
 
   module procedure to_inference_engine
-    inference_engine = inference_engine_t(self%metadata_%strings(), self%w, self%b, self%n, self%input_range_, self%output_range_)
+    inference_engine = inference_engine_t(self%metadata_%strings(), self%w, self%b, self%n, self%input_map_, self%output_map_)
   end procedure
 
   module procedure perturbed_identity_network
@@ -316,7 +316,7 @@
         )
           trainable_engine = trainable_engine_t( &
             nodes = n, weights = w, biases = b, differentiable_activation_strategy = activation, metadata = metadata, &
-            input_range = input_range, output_range = output_range &
+            input_map = input_map, output_map = output_map &
           )
         end associate
       end associate
@@ -339,8 +339,8 @@ pure function e(j,n) result(unit_vector)
       expected_outputs => input_output_pair%expected_outputs() &
     )
       associate( &
-         normalized_inputs => self%input_range_%map_to_training_range(inputs), &
-         normalized_outputs => self%output_range_%map_to_training_range(expected_outputs) &
+         normalized_inputs => self%input_map_%map_to_training_range(inputs), &
+         normalized_outputs => self%output_map_%map_to_training_range(expected_outputs) &
       )
         normalized_input_output_pair = input_output_pair_t(normalized_inputs, normalized_outputs)
       end associate
@@ -348,19 +348,19 @@ pure function e(j,n) result(unit_vector)
   end procedure
 
   module procedure map_to_input_training_range
-    normalized_tensor = self%input_range_%map_to_training_range(tensor)
+    normalized_tensor = self%input_map_%map_to_training_range(tensor)
   end procedure
 
   module procedure map_from_input_training_range
-    unnormalized_tensor = self%input_range_%map_from_training_range(tensor)
+    unnormalized_tensor = self%input_map_%map_from_training_range(tensor)
   end procedure
   
   module procedure map_to_output_training_range
-    normalized_tensor = self%output_range_%map_to_training_range(tensor)
+    normalized_tensor = self%output_map_%map_to_training_range(tensor)
   end procedure
 
   module procedure map_from_output_training_range
-    unnormalized_tensor = self%output_range_%map_from_training_range(tensor)
+    unnormalized_tensor = self%output_map_%map_from_training_range(tensor)
   end procedure
   
 
diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90
index c76e04f3e..50f3cff31 100644
--- a/src/inference_engine_m.f90
+++ b/src/inference_engine_m.f90
@@ -17,7 +17,7 @@ module inference_engine_m
  use step_m, only : step_t
  use swish_m, only : swish_t
  use tensor_m, only : tensor_t
- use tensor_range_m, only : tensor_range_t, phase_space_bin_t
+ use tensor_map_m, only : tensor_map_t
  use trainable_engine_m, only : trainable_engine_t
  use training_configuration_m, only : training_configuration_t
  use ubounds_m, only : ubounds_t
diff --git a/test/main.F90 b/test/main.F90
index e6ecdcf0b..ba9fe4f40 100644
--- a/test/main.F90
+++ b/test/main.F90
@@ -8,7 +8,7 @@ program main
   use hyperparameters_test_m, only : hyperparameters_test_t
   use network_configuration_test_m, only : network_configuration_test_t
   use training_configuration_test_m, only : training_configuration_test_t
-  use tensor_range_test_m, only : tensor_range_test_t
+  use tensor_map_test_m, only : tensor_map_test_t
   use julienne_m, only : command_line_t
   implicit none
 
@@ -19,7 +19,7 @@ program main
   type(metadata_test_t) metadata_test
   type(network_configuration_test_t) network_configuration_test
   type(training_configuration_test_t) training_configuration_test
-  type(tensor_range_test_t) tensor_range_test
+  type(tensor_map_test_t) tensor_map_test
   real t_start, t_finish
 
   integer :: passes=0, tests=0
@@ -43,7 +43,7 @@ program main
   call network_configuration_test%report(passes, tests)
   call metadata_test%report(passes, tests)
   call training_configuration_test%report(passes, tests)
-  call tensor_range_test%report(passes, tests)
+  call tensor_map_test%report(passes, tests)
   call asymmetric_engine_test%report(passes, tests)
   call inference_engine_test%report(passes, tests)
   call trainable_engine_test%report(passes, tests)
diff --git a/test/tensor_range_test_m.F90 b/test/tensor_range_test_m.F90
index 2b4259493..b99c31d3b 100644
--- a/test/tensor_range_test_m.F90
+++ b/test/tensor_range_test_m.F90
@@ -1,25 +1,25 @@
 ! Copyright (c), The Regents of the University of California
 ! Terms of use are as specified in LICENSE.txt
-module tensor_range_test_m
-  !! Test tensor_range_t object input/output, construction, and linear mappings
+module tensor_map_test_m
+  !! Test tensor_map_t object input/output, construction, and linear mappings
 
   ! External dependencies
   use assert_m, only : assert
   use julienne_m, only : string_t, test_t, test_result_t, test_description_t, test_description_substring, file_t
-  use inference_engine_m, only : tensor_range_t, tensor_t
+  use inference_engine_m, only : tensor_map_t, tensor_t
 #ifdef __GFORTRAN__
   use julienne_m, only : test_function_i
 #endif
 
   ! Internal dependencies
-  use tensor_range_m, only : tensor_range_t
+  use tensor_map_m, only : tensor_map_t
 
   implicit none
 
   private
-  public :: tensor_range_test_t
+  public :: tensor_map_test_t
 
-  type, extends(test_t) :: tensor_range_test_t
+  type, extends(test_t) :: tensor_map_test_t
   contains
     procedure, nopass :: subject
     procedure, nopass :: results
@@ -29,7 +29,7 @@ module tensor_range_test_m
 
   pure function subject() result(specimen)
     character(len=:), allocatable :: specimen
-    specimen = "A tensor_range_t object"
+    specimen = "A tensor_map_t object"
   end function
 
   function results() result(test_results)
@@ -38,12 +38,12 @@ function results() result(test_results)
 
 #ifndef __GFORTRAN__
     test_descriptions = [ & 
-      test_description_t("component-wise construction followed by conversion to and from JSON", write_then_read_tensor_range), &
+      test_description_t("component-wise construction followed by conversion to and from JSON", write_then_read_tensor_map), &
       test_description_t("mapping to and from the unit interval as an identity transformation", map_to_from_training_range) &
     ]
 #else
     procedure(test_function_i), pointer :: check_write_then_read_ptr, check_map_to_from_range_ptr 
-    check_write_then_read_ptr   => write_then_read_tensor_range
+    check_write_then_read_ptr   => write_then_read_tensor_map
     check_map_to_from_range_ptr => map_to_from_training_range
 
     test_descriptions = [ &
@@ -60,17 +60,17 @@ function results() result(test_results)
     test_results = test_descriptions%run()
   end function
 
-  function write_then_read_tensor_range() result(test_passes)
+  function write_then_read_tensor_map() result(test_passes)
     logical test_passes
     type(file_t) :: json_file
 #ifdef _CRAYFTN
-    type(tensor_range_t) :: tensor_range
-    tensor_range = tensor_range_t(layer="inputs", minima=[-1., 0., 1.], maxima=[1., 2., 4.])
+    type(tensor_map_t) :: tensor_map
+    tensor_map = tensor_map_t(layer="inputs", minima=[-1., 0., 1.], maxima=[1., 2., 4.])
 #else
-    associate(tensor_range => tensor_range_t(layer="inputs", minima=[-1., 0., 1.], maxima=[1., 2., 4.]))
+    associate(tensor_map => tensor_map_t(layer="inputs", minima=[-1., 0., 1.], maxima=[1., 2., 4.]))
 #endif
-      associate(from_json => tensor_range_t(tensor_range%to_json()))
-        test_passes = tensor_range == from_json
+      associate(from_json => tensor_map_t(tensor_map%to_json()))
+        test_passes = tensor_map == from_json
       end associate
 #ifndef _CRAYFTN
     end associate
@@ -81,16 +81,16 @@ function map_to_from_training_range() result(test_passes)
     logical test_passes
     real, parameter :: tolerance = 1.E-07
 #ifdef _CRAYFTN
-    type(tensor_range_t) :: tensor_range
+    type(tensor_map_t) :: tensor_map
     type(tensor_t) :: tensor, round_trip
-    tensor_range = tensor_range_t(layer="output", minima=[-4., 0., 1., -1.], maxima=[0., 2., 5., 1.])
+    tensor_map = tensor_map_t(layer="output", minima=[-4., 0., 1., -1.], maxima=[0., 2., 5., 1.])
     tensor = tensor_t([-2., 0., 5., 0.])
-    round_trip = tensor_range%map_from_training_range(tensor_range%map_to_training_range(tensor))
+    round_trip = tensor_map%map_from_training_range(tensor_map%map_to_training_range(tensor))
     test_passes = all(abs(tensor%values() - round_trip%values()) < tolerance)
 #else
-    associate(tensor_range => tensor_range_t(layer="output", minima=[-4., 0., 1., -1.], maxima=[0., 2., 5., 1.]))
+    associate(tensor_map => tensor_map_t(layer="output", minima=[-4., 0., 1., -1.], maxima=[0., 2., 5., 1.]))
       associate(tensor => tensor_t([-2., 0., 5., 0.]))
-        associate(round_trip => tensor_range%map_from_training_range(tensor_range%map_to_training_range(tensor)))
+        associate(round_trip => tensor_map%map_from_training_range(tensor_map%map_to_training_range(tensor)))
           test_passes = all(abs(tensor%values() - round_trip%values()) < tolerance)
         end associate
       end associate
@@ -98,4 +98,4 @@ function map_to_from_training_range() result(test_passes)
 #endif
   end function
 
-end module tensor_range_test_m
+end module tensor_map_test_m
