<commit_before>/* Copyright 2017, <NAME>. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.platanios.tensorflow.api.learn.layers

import org.platanios.tensorflow.api.core.{Graph, Shape}
import org.platanios.tensorflow.api.learn._
import org.platanios.tensorflow.api.ops.variables.Variable.VariableGetter
import org.platanios.tensorflow.api.ops.variables.VariableScope.maybeWrapCustomVariableGetter
import org.platanios.tensorflow.api.ops.{Op, OpSpecification}
import org.platanios.tensorflow.api.ops.variables._
import org.platanios.tensorflow.api.types.DataType

import scala.collection.generic.CanBuildFrom
import scala.collection.{TraversableLike, mutable}
import scala.util.DynamicVariable

/**
  * @author <NAME>
  */
abstract class Layer[T, R](
    protected val name: String
)(implicit
    context: DynamicVariable[LayerCreationContext]
) {
  val uniquifiedName: String = Layer.uniqueName(name)

  private val variableScope: VariableScope = {
    if (context.value.variableScope.name != "") {
      context.value.variableScope
    } else {
      VariableScope.createWithVariableScope(name)(Op.currentVariableScope)
    }
  }

  val layerType: String

  def forward(input: T, mode: Mode): LayerInstance[T, R]

  def apply(input: T, mode: Mode): LayerInstance[T, R] = Op.createWith(
    nameScope = context.value.nameScope,
    device = context.value.device,
    deviceFunction = context.value.deviceFunction
  ) {
    forward(input, mode)
  }

  def >>[S](other: Layer[R, S]): Compose[T, R, S] = compose(other)

  def +(other: Layer[T, R]): Concatenate[T, R] = concatenate(other)

  def ++(others: Seq[Layer[T, R]]): Concatenate[T, R] = concatenate(others: _*)

  def compose[S](other: Layer[R, S]): Compose[T, R, S] = Compose(this, other)

  def concatenate(others: Layer[T, R]*): Concatenate[T, R] = Concatenate(this +: others)

  protected def variable(
      name: String, dataType: DataType = null, shape: Shape = null, initializer: Initializer = null,
      regularizer: Regularizer = null, trainable: Boolean = true, reuse: Reuse = ReuseOrCreateNew,
      collections: Set[Graph.Key[Variable]] = Set.empty, cachingDevice: OpSpecification => String = null): Variable = {
    variableScope.getVariable(
      Op.currentVariableStore, name, dataType, shape, initializer, regularizer, trainable, reuse, collections,
      cachingDevice)
  }

  protected def partitionedVariable(
      name: String, dataType: DataType = null, shape: Shape = null, initializer: Initializer = null,
      regularizer: Regularizer = null, partitioner: Partitioner = null, trainable: Boolean = true,
      reuse: Reuse = ReuseOrCreateNew, collections: Set[Graph.Key[Variable]] = Set.empty,
      cachingDevice: OpSpecification => String = null): PartitionedVariable = {
    variableScope.getPartitionedVariable(
      Op.currentVariableStore, name, dataType, shape, initializer, regularizer, partitioner, trainable, reuse,
      collections, cachingDevice)
  }

  override def toString: String = s"$uniquifiedName[$layerType]"
}

private[api] final case class LayerCreationContext(
    nameScope: String = "", variableScope: VariableScope = VariableScope(reuse = ReuseOrCreateNew),
    device: String = "", deviceFunction: OpSpecification => String = _.device)

object Layer {
  trait API {
    def currentNameScope: String = Layer.currentNameScope
    def currentVariableScope: VariableScope = Layer.currentVariableScope
    def currentDevice: String = Layer.currentDevice
    def currentDeviceFunction: OpSpecification => String = Layer.currentDeviceFunction

    def createWith[R](
        nameScope: String = null,
        device: String = "",
        deviceFunction: OpSpecification => String = _.device,
    )(block: => R): R = {
      Layer.createWith(nameScope, device, deviceFunction)(block)
    }

    def nameScope[R](nameScope: String)(block: => R): R = Layer.createWith(nameScope = nameScope)(block)

    def device[R](device: String)(block: => R): R = Layer.createWith(device = device)(block)

    def deviceFunction[R](deviceFunction: OpSpecification => String)(block: => R): R = {
      Layer.createWith(deviceFunction = deviceFunction)(block)
    }

    def variableScope[R](
        name: String, reuse: ReuseAllowed = ReuseOrCreateNew, dataType: DataType = null,
        initializer: Initializer = null, regularizer: Regularizer = null, partitioner: Partitioner = null,
        cachingDevice: OpSpecification => String = null, customGetter: VariableGetter = null,
        isDefaultName: Boolean = false, isPure: Boolean = false
    )(block: => R): R = {
      Layer.createWithVariableScope(
        name, reuse, dataType, initializer, regularizer, partitioner, cachingDevice, customGetter, isPure)(block)
    }

    def updatedVariableScope[R](
        variableScope: VariableScope, reuse: ReuseAllowed = ReuseOrCreateNew, dataType: DataType = null,
        initializer: Initializer = null, regularizer: Regularizer = null, partitioner: Partitioner = null,
        cachingDevice: OpSpecification => String = null, customGetter: VariableGetter = null, isPure: Boolean = false
    )(block: => R): R = {
      Layer.createWithUpdatedVariableScope(<commit_msg>Minor edit for compatibility with Scala 2.11.<commit_after>/* Copyright 2017, <NAME>. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.platanios.tensorflow.api.learn.layers

import org.platanios.tensorflow.api.core.{Graph, Shape}
import org.platanios.tensorflow.api.learn._
import org.platanios.tensorflow.api.ops.variables.Variable.VariableGetter
import org.platanios.tensorflow.api.ops.variables.VariableScope.maybeWrapCustomVariableGetter
import org.platanios.tensorflow.api.ops.{Op, OpSpecification}
import org.platanios.tensorflow.api.ops.variables._
import org.platanios.tensorflow.api.types.DataType

import scala.collection.generic.CanBuildFrom
import scala.collection.{TraversableLike, mutable}
import scala.util.DynamicVariable

/**
  * @author <NAME>
  */
abstract class Layer[T, R](
    protected val name: String
)(implicit
    context: DynamicVariable[LayerCreationContext]
) {
  val uniquifiedName: String = Layer.uniqueName(name)

  private val variableScope: VariableScope = {
    if (context.value.variableScope.name != "") {
      context.value.variableScope
    } else {
      VariableScope.createWithVariableScope(name)(Op.currentVariableScope)
    }
  }

  val layerType: String

  def forward(input: T, mode: Mode): LayerInstance[T, R]

  def apply(input: T, mode: Mode): LayerInstance[T, R] = Op.createWith(
    nameScope = context.value.nameScope,
    device = context.value.device,
    deviceFunction = context.value.deviceFunction
  ) {
    forward(input, mode)
  }

  def >>[S](other: Layer[R, S]): Compose[T, R, S] = compose(other)

  def +(other: Layer[T, R]): Concatenate[T, R] = concatenate(other)

  def ++(others: Seq[Layer[T, R]]): Concatenate[T, R] = concatenate(others: _*)

  def compose[S](other: Layer[R, S]): Compose[T, R, S] = Compose(this, other)

  def concatenate(others: Layer[T, R]*): Concatenate[T, R] = Concatenate(this +: others)

  protected def variable(
      name: String, dataType: DataType = null, shape: Shape = null, initializer: Initializer = null,
      regularizer: Regularizer = null, trainable: Boolean = true, reuse: Reuse = ReuseOrCreateNew,
      collections: Set[Graph.Key[Variable]] = Set.empty, cachingDevice: OpSpecification => String = null): Variable = {
    variableScope.getVariable(
      Op.currentVariableStore, name, dataType, shape, initializer, regularizer, trainable, reuse, collections,
      cachingDevice)
  }

  protected def partitionedVariable(
      name: String, dataType: DataType = null, shape: Shape = null, initializer: Initializer = null,
      regularizer: Regularizer = null, partitioner: Partitioner = null, trainable: Boolean = true,
      reuse: Reuse = ReuseOrCreateNew, collections: Set[Graph.Key[Variable]] = Set.empty,
      cachingDevice: OpSpecification => String = null): PartitionedVariable = {
    variableScope.getPartitionedVariable(
      Op.currentVariableStore, name, dataType, shape, initializer, regularizer, partitioner, trainable, reuse,
      collections, cachingDevice)
  }

  override def toString: String = s"$uniquifiedName[$layerType]"
}

private[api] final case class LayerCreationContext(
    nameScope: String = "", variableScope: VariableScope = VariableScope(reuse = ReuseOrCreateNew),
    device: String = "", deviceFunction: OpSpecification => String = _.device)

object Layer {
  trait API {
    def currentNameScope: String = Layer.currentNameScope
    def currentVariableScope: VariableScope = Layer.currentVariableScope
    def currentDevice: String = Layer.currentDevice
    def currentDeviceFunction: OpSpecification => String = Layer.currentDeviceFunction

    def createWith[R](
        nameScope: String = null,
        device: String = "",
        deviceFunction: OpSpecification => String = op => op.device,
    )(block: => R): R = {
      Layer.createWith(nameScope, device, deviceFunction)(block)
    }

    def nameScope[R](nameScope: String)(block: => R): R = Layer.createWith(nameScope = nameScope)(block)

    def device[R](device: String)(block: => R): R = Layer.createWith(device = device)(block)

    def deviceFunction[R](deviceFunction: OpSpecification => String)(block: => R): R = {
      Layer.createWith(deviceFunction = deviceFunction)(block)
    }

    def variableScope[R](
        name: String, reuse: ReuseAllowed = ReuseOrCreateNew, dataType: DataType = null,
        initializer: Initializer = null, regularizer: Regularizer = null, partitioner: Partitioner = null,
        cachingDevice: OpSpecification => String = null, customGetter: VariableGetter = null,
        isDefaultName: Boolean = false, isPure: Boolean = false
    )(block: => R): R = {
      Layer.createWithVariableScope(
        name, reuse, dataType, initializer, regularizer, partitioner, cachingDevice, customGetter, isPure)(block)
    }

    def updatedVariableScope[R](
        variableScope: VariableScope, reuse: ReuseAllowed = ReuseOrCreateNew, dataType: DataType = null,
        initializer: Initializer = null, regularizer: Regularizer = null, partitioner: Partitioner = null,
        cachingDevice: OpSpecification => String = null, customGetter: VariableGetter = null, isPure: Boolean = false
    )(block: => R): R = {
      Layer.createWithUpdatedVariableScope(
{'fraction_non_alphanumeric': 0.11030630945108648, 'fraction_numerical': 0.0016580853477615848, 'mean_word_length': 4.312934631432546, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 40, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2371945', 'n_tokens_mistral': 3404, 'n_tokens_neox': 3274, 'n_words': 904}