<commit_before>export { ScaleTranslateArraySet };

import * as FloatValue from "../Unpacker/FloatValue.js";
import * as ConvBiasActivation from "./ConvBiasActivation.js";

/**
 * Several scale-translate for escaping a value bounds from being activated (i.e. being non-linearized) by activation function.
 *
 *
 * @member {FloatValue.ScaleTranslateArray} doWithoutPreviousUndo
 *   The scale-translate for moving current value bounds into the linear domain of the activation function. That is, for letting
 * ConvBiasActivation.BoundsArraySet.beforeActivation escape from activation function's non-linear domain into linear domain.
 *
 * @member {FloatValue.ScaleTranslateArray} do
 *   The result of combining this.doWithoutPreviousUndo with previous ActivationEscape.ScaleTranslateSet.undo. It both undo the
 * previous escaping scale-translate and do itself escaping scale-translate.
 *
 * @member {FloatValue.ScaleTranslateArray} undo
 *   If apply this.undo (important: scale first, translate second), it will have the effect of undoing the this.do.
 */
class ScaleTranslateArraySet {

  constructor() {
    this.doWithoutPreviousUndo = new FloatValue.ScaleTranslateArray();
    this.do = new FloatValue.ScaleTranslateArray();
    this.undo = new FloatValue.ScaleTranslateArray();
  }

  clone() {
    let result = new ScaleTranslateArraySet();
    result.set_byScaleTranslateSet( this );
    return result;
  }

  /**
   * @param {ScaleTranslateArraySet} aScaleTranslateSet
   *   The ScaleTranslateSet to be copied.
   */
  set_byScaleTranslateArraySet( aScaleTranslateArraySet ) {
    this.doWithoutPreviousUndo.set_all_byScaleTranslateArray( aScaleTranslateArraySet.doWithoutPreviousUndo );
    this.do.set_all_byScaleTranslateArray( aScaleTranslateArraySet.do );
    this.undo.set_all_byScaleTranslateArray( aScaleTranslateArraySet.undo );
  }

  /** Reset all scale-translate values. Default is ( scale = 1, translate = 0 ) (i.e. no scale and no translate). */
  reset_by_scale_translate( scale = 1, translate = 0 ) {
    this.doWithoutPreviousUndo.set_all_by_scale_translate( scale , translate );
    this.do.set_all_by_scale_translate( scale , translate );
    this.undo.set_all_by_scale_translate( scale , translate );
  }

//!!! ...unfinished... (2021/12/26)
/**
 * - For depthwise with ( pad == same ), it seems that the activation escaping can not be undone completely, because pad is always 0
 *   (can not scale-translate). Unless, pad can be non-zero?
 *
 *   - Fortunately, this is not a problem for pass-through channels. Because the pass-through filter value is also 0 in the padded
 *       position, the result is not affected. (But its a big problem for non-pass-through channels.)
 *
 *   - Perhaps, force use ( pad == valid ) so that the activation escaping always can be undone completely.
 *
 *     - In original ShuffleNetV2, using ( pad == same ) is necessary for concatenating ( depthwise1, input1 ).
 *
 *     - In our combined-depthwise1-with-higher-half-pass-through (i.e. ONE_INPUT_HALF_THROUGH_XXX), the ( depthwise1, input1 )
 *         already combined together. There is no concatenation issue. So using ( pad == valid ) is possible.
 *
 *     - However, this is not so good for pass-through channels. Because the pass-through filter value is 0 at the right-bottom
 *         corner, the image value will be destroyed.
 *
 * - If previous convolution does not have activation escaping do (i.e. is a normal convolution without pass-through)，this
 *     this convolution's .do should be different.
 *
 * - Perhaps, there should be value-bounds and activation-escaping scale-translate for every single channel (i.e. lower-array,
 *     upper-array, scale-array and translate-array). Even if channels are shuffled, they could be still tracked correctly.
 *
 * - When extractFilters() and extractBiases(), pre-apply the per channel undoing scale and translate to filter-value, bias-value,
 *     and their bounds. (The .do and .undo should also affect the value bounds.)
 *
 * - When across Block (i.e. at ShuffleNetV2_ByMobileNetV1's head), the higher-half-copy-lower-half channels's value bounds
 *     does not come from previous corresponding channels. They comes from the lower-half channels which they copied from.
 *
 */

  /**
   * Set the following properties:
   *   - this.doWithoutPreviousUndo
   *   - this.do
   *   - this.undo
   *
   * @param {ConvBiasActivation.BoundsArraySet} current_ConvBiasActivation_BoundsArraySet
   *   The BoundsArraySet of current convolution-bias-activation for calculating this.doWithoutPreviousUndo.
   *
   * @param {ScaleTranslateArraySet} previous_ActivationEscaping_ScaleTranslateArraySet
   *   The ActivationEscaping.ScaleTranslateArraySet of previous convolution-bias-activation for calculating this.do.
   */
  set_by_currentBoundsArraySet_previousActivationEscaping(
    current_ConvBiasActivation_BoundsArraySet, previous_ActivationEscaping_ScaleTranslateArraySet ) {

    // Calculate the scale-translate for escaping from activation function's non-linear domain into linear domain.
    //
    // Note: This does not work for avg/max pooling.
    this.doWithoutPreviousUndo.set_all_by_fromBoundsArray_ToBoundsArray(
      current_ConvBiasActivation_BoundsArraySet.beforeActivation, current_ConvBiasActivation_BoundsArraySet.output );

    // Combine undoing previous activation escaping scale-translate and doing current activation escaping scale-translate.
    this.do.set_all_byScaleTranslateArray( previous_ActivationEscaping_ScaleTranslateArraySet.undo );
    this.do.scaleTranslate_all_byScaleTranslateArray( this.doWithoutPreviousUndo );

    // Prepare the undoing scale-translate for the next convolution-bias-activation.
    this.undo.set_all_byUndo_ScaleTranslateArray( this.do );
  }

}

/**
 * Suppose
 *   - The previous PointDepthPoint (or convolution-bias-activation)
 *     - output channel count is q.
 *     - .output is W = ( w1, w2, ..., wq ).
 *
 *
 * 1.
 *
 * Suppose
 *   - This pointwise1
 *     - input channel count is q.
 *     - per output channel filter weights are Q = ( Q1, Q2, ..., Qq ).
 *     - per output channel bias weights are R.
 *     - activation escaping is ( scale = A, translate = B )
 *     - per channel .beforeActivation is X = ( x1, x2, ..., xq ) = W * Q' + R'.
 *     - per channel .output is ( AX + B )
 *
 * Find out Q' and R' so that X = A * ( W * Q + R ) + B.
 *   X = A * ( W * Q + R ) + B
 *     = A * W * Q + A * R + B
 *     = W * ( A * Q ) + ( A * R  + B )
 *     = W * Q' + R'
 *
 *  Q' = A * Q
 *  R' = A * R  + B
 *

//!!! ...unfinished... (2022/01/05)

 * 1.
 *
 * Suppose
 *   - This pointwise1
 *     - input channel count is q.
 *     - per output channel filter weights are Q = ( Q1, Q2, ..., Qq ).
 *     - per output channel bias weights are R.
 *     - activation escaping is ( scale = A, translate = B )
 *     - per channel .beforeActivation is X = ( x1, x2, ..., xq ) = W * Q + R.
 *     - per channel .output is ( AX + B )
 *
 *   - This depthwise
 *     - filter size is s.
 *     - per output channel filter weights are S = ( S1, S2, ..., Ss ).
 *     - per output channel bias weights are T.
 *     - activation escaping is ( scale = C, translate = D )
 *     - per channel .beforeActivation is Y = ( y1, y2, ..., ys ) = ( AX + B ) * S + T.
 *     - per channel .output is ( CY + D )
 *
 *   - This pointwise2 (always has bias, always has no activation)
 *     - input channel count is u.
 *     - per input channel extra scale-translate is ( scale = E, translate = F )
 *     - per output channel filter weights are U = ( U1, U2, ..., Uu ).
 *     - per output channel bias weights are F + V.
 *     - per channel .output is Z = ( z1, z2, ..., zu ) = ( CY + D ) * ( E * U ) + ( F + V ).
 *
 * Find out the extra scale-translate ( E, F ) so that Z = ( ( W * Q + R ) * S + T ) * U + V
 *   Z = ( CY + D ) * ( E * U ) + ( F + V )
 *     = ( C * ( ( AX + B ) * S + T ) + D ) * ( E * U ) + ( F + V )
 *     = ( C * ( ( A * ( W * Q + R ) + B ) * S + T ) + D ) * ( E * U ) + ( F + V )
 *     =
 *
 *
 *
 *

//!!!
 *   - This pointwise (or depthwise) filter weights are U = ( U1, U2, ... Um ).
 *   - This pointwise (or depthwise) filter weights are U = ( U1, U2, ... Um ).
 *   - This pointwise input channel count is m. (Or, this depthwise filter size is m.)
 *   - This pointwise (or depthwise) filter weights are U = ( U1, U2, ... Um ).
 *   - This pointwise (or depthwise) bias weights are V.
 *
 * This pointwise (or depthwise) .beforeActivation will be:
 *   Y = ( AX + B ) * U + V
 *     = ( ( Ax1 + B ) * U1 ) + ( ( Ax2 + B ) * U2 ) + ... + ( ( Axm + B ) * Um ) + V
 *     = ( AU1x1 + BU1 ) + ( AU2x2 + BU2 ) + ... + ( AUmxm + BUm ) + V
 *     = ( AU1x1 + AU2x2 + ... + AUmxm ) + ( BU1 + BU2 + ... + BUm ) + V
 *     = A * ( U1x1 + U2x2 + ... + Umxm ) + B * ( U1 + U2 + ... + Um ) + V
 *
 * Suppose
 *   - This pointwise (or depthwise) activation escaping is ( scale = C, translate = D )
 *
 *
 * This pointwise (or depthwise) .output will be:
 *   Z = CY + D
 *     = C * ( A * ( U1x1 + U2x2 + ... + Umxm ) + B * ( U1 + U2 + ... + Um ) + V ) + D
 *     = AC * ( U1x1 + U2x2 + ... + Umxm ) + C * ( B * ( U1 + U2 + ... + Um ) + V ) + D
 *
 * How to get back ( UX + V ) from Z?
 *   ( UX + V ) = ( U1x1 + U2x2 + ... + Umxm ) + V
 *
 * Let:
 *   - undo.scale = 1 / AC
 *   - undo.translate = V - ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale
 *
 * Apply ( undo.scale, undo.translate ) to Z:
 *   ( Z * undo.scale ) + undo.translate
 *   = ( AC * ( U1x1 + U2x2 + ... + Umxm ) + C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale + undo.translate
 *   = AC * ( U1x1 + U2x2 + ... + Umxm ) * undo.scale + ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale + undo.translate
 *   = AC * ( U1x1 + U2x2 + ... + Umxm ) / AC + ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale + undo.translate
 *   = ( U1x1 + U2x2 + ... + Umxm ) + ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale + undo.translate
 *   = ( U1x1 + U2x2 + ... + Umxm ) + ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale + V
 *                                  - ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale
 *   = ( U1x1 + U2x2 + ... + Umxm ) + V
 *   = ( UX + V )
 *

//!!! (2022/01/05 Remarked) Old Wrong!
//  *   - undo.scale = 1 / ( AU1 + AU2 + ... + AUm )
//  *   - undo.translate = D - ( C * ( BU1 + BU2 + ... + BUm + V ) / ( AU1 + AU2 + ... + AUm ) ) - ( D / ( AU1 + AU2 + ... + AUm ) )
//  *                    = D - ( C * ( BU1 + BU2 + ... + BUm + V ) * undo.scale ) - ( D * undo.scale )
//  *                    = D - ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale
//  *
//  * Apply ( undo.scale, undo.translate ) to Z:
//  *   ( Z * undo.scale ) + undo.translate
//  *   = ( C * ( AU1 + AU2 + ... + AUm ) * x + ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) ) * undo.scale + undo.translate
//  *   = ( C * ( AU1 + AU2 + ... + AUm ) * x * undo.scale ) + ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale ) + undo.translate
//  *   = ( C * ( AU1 + AU2 + ... + AUm ) * x / ( AU1 + AU2 + ... + AUm ) ) + ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale ) + undo.translate
//  *   = Cx + ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale ) + undo.translate
//  *   = Cx + ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale ) + ( D - ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale )
//  *   = Cx + ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale ) + D
//  *        - ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale )
//  *   = Cx + D
 *
 *
 * 2. What if this pointwise (or depthwise) does not have filter weights ( U1, U2, ... Um )? (e.g avg/max pooling)
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 * 3. What if this pointwise (or depthwise) does not have bias weights V?
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
<commit_msg>Update ActivationEscaping.js<commit_after>export { ScaleTranslateArraySet };

import * as FloatValue from "../Unpacker/FloatValue.js";
import * as ConvBiasActivation from "./ConvBiasActivation.js";

/**
 * Several scale-translate for escaping a value bounds from being activated (i.e. being non-linearized) by activation function.
 *
 *
 * @member {FloatValue.ScaleTranslateArray} doWithoutPreviousUndo
 *   The scale-translate for moving current value bounds into the linear domain of the activation function. That is, for letting
 * ConvBiasActivation.BoundsArraySet.beforeActivation escape from activation function's non-linear domain into linear domain.
 *
 * @member {FloatValue.ScaleTranslateArray} do
 *   The result of combining this.doWithoutPreviousUndo with previous ActivationEscape.ScaleTranslateSet.undo. It both undo the
 * previous escaping scale-translate and do itself escaping scale-translate.
 *
 * @member {FloatValue.ScaleTranslateArray} undo
 *   If apply this.undo (important: scale first, translate second), it will have the effect of undoing the this.do.
 */
class ScaleTranslateArraySet {

  constructor() {
    this.doWithoutPreviousUndo = new FloatValue.ScaleTranslateArray();
    this.do = new FloatValue.ScaleTranslateArray();
    this.undo = new FloatValue.ScaleTranslateArray();
  }

  clone() {
    let result = new ScaleTranslateArraySet();
    result.set_byScaleTranslateSet( this );
    return result;
  }

  /**
   * @param {ScaleTranslateArraySet} aScaleTranslateSet
   *   The ScaleTranslateSet to be copied.
   */
  set_byScaleTranslateArraySet( aScaleTranslateArraySet ) {
    this.doWithoutPreviousUndo.set_all_byScaleTranslateArray( aScaleTranslateArraySet.doWithoutPreviousUndo );
    this.do.set_all_byScaleTranslateArray( aScaleTranslateArraySet.do );
    this.undo.set_all_byScaleTranslateArray( aScaleTranslateArraySet.undo );
  }

  /** Reset all scale-translate values. Default is ( scale = 1, translate = 0 ) (i.e. no scale and no translate). */
  reset_by_scale_translate( scale = 1, translate = 0 ) {
    this.doWithoutPreviousUndo.set_all_by_scale_translate( scale , translate );
    this.do.set_all_by_scale_translate( scale , translate );
    this.undo.set_all_by_scale_translate( scale , translate );
  }

//!!! ...unfinished... (2021/12/26)
/**
 * - For depthwise with ( pad == same ), it seems that the activation escaping can not be undone completely, because pad is always 0
 *   (can not scale-translate). Unless, pad can be non-zero?
 *
 *   - Fortunately, this is not a problem for pass-through channels. Because the pass-through filter value is also 0 in the padded
 *       position, the result is not affected. (But its a big problem for non-pass-through channels.)
 *
 *   - Perhaps, force use ( pad == valid ) so that the activation escaping always can be undone completely.
 *
 *     - In original ShuffleNetV2, using ( pad == same ) is necessary for concatenating ( depthwise1, input1 ).
 *
 *     - In our combined-depthwise1-with-higher-half-pass-through (i.e. ONE_INPUT_HALF_THROUGH_XXX), the ( depthwise1, input1 )
 *         already combined together. There is no concatenation issue. So using ( pad == valid ) is possible.
 *
 *     - However, this is not so good for pass-through channels. Because the pass-through filter value is 0 at the right-bottom
 *         corner, the image value will be destroyed.
 *
 * - If previous convolution does not have activation escaping do (i.e. is a normal convolution without pass-through)，this
 *     this convolution's .do should be different.
 *
 * - Perhaps, there should be value-bounds and activation-escaping scale-translate for every single channel (i.e. lower-array,
 *     upper-array, scale-array and translate-array). Even if channels are shuffled, they could be still tracked correctly.
 *
 * - When extractFilters() and extractBiases(), pre-apply the per channel undoing scale and translate to filter-value, bias-value,
 *     and their bounds. (The .do and .undo should also affect the value bounds.)
 *
 * - When across Block (i.e. at ShuffleNetV2_ByMobileNetV1's head), the higher-half-copy-lower-half channels's value bounds
 *     does not come from previous corresponding channels. They comes from the lower-half channels which they copied from.
 *
 */

  /**
   * Set the following properties:
   *   - this.doWithoutPreviousUndo
   *   - this.do
   *   - this.undo
   *
   * @param {ConvBiasActivation.BoundsArraySet} current_ConvBiasActivation_BoundsArraySet
   *   The BoundsArraySet of current convolution-bias-activation for calculating this.doWithoutPreviousUndo.
   *
   * @param {ScaleTranslateArraySet} previous_ActivationEscaping_ScaleTranslateArraySet
   *   The ActivationEscaping.ScaleTranslateArraySet of previous convolution-bias-activation for calculating this.do.
   */
  set_by_currentBoundsArraySet_previousActivationEscaping(
    current_ConvBiasActivation_BoundsArraySet, previous_ActivationEscaping_ScaleTranslateArraySet ) {

    // Calculate the scale-translate for escaping from activation function's non-linear domain into linear domain.
    //
    // Note: This does not work for avg/max pooling.
    this.doWithoutPreviousUndo.set_all_by_fromBoundsArray_ToBoundsArray(
      current_ConvBiasActivation_BoundsArraySet.beforeActivation, current_ConvBiasActivation_BoundsArraySet.output );

    // Combine undoing previous activation escaping scale-translate and doing current activation escaping scale-translate.
    this.do.set_all_byScaleTranslateArray( previous_ActivationEscaping_ScaleTranslateArraySet.undo );
    this.do.scaleTranslate_all_byScaleTranslateArray( this.doWithoutPreviousUndo );

    // Prepare the undoing scale-translate for the next convolution-bias-activation.
    this.undo.set_all_byUndo_ScaleTranslateArray( this.do );
  }

}

/**
 * Suppose
 *   - The previous PointDepthPoint (or convolution-bias-activation)
 *     - output channel count is q.
 *     - .output is W = ( w1, w2, ..., wq ).
 *
 *
 * 1.
 *
 * Suppose
 *   - This pointwise1
 *     - input channel count is q.
 *     - per output channel filter weights are Q = ( Q1, Q2, ..., Qq ) and will be modified as Q' = ( Q1', Q2', ..., Qq' ).
 *     - per output channel bias weights are R and will be modified as R'.
 *     - activation escaping is ( scale = A, translate = B )
 *     - per output channel original is X  = ( x1 , x2 , ..., xq  ) = W * Q  + R
 *     - per output channel modified is X' = ( x1', x2', ..., xq' ) = W * Q' + R'
 *
 * Find out Q' and R' so that X' = ( AX + B )
 *   X' = ( AX + B )
 *      = A * ( W * Q + R ) + B
 *      = A * W * Q + A * R + B
 *      = W * ( A * Q ) + ( A * R  + B )
 *      = W * Q' + R'
 *
 * Got
 *  Q' = A * Q
 *  R' = A * R + B
 *

//!!! ...unfinished... (2022/01/05)

 * 1.
 *
 * Suppose
 *   - This pointwise1
 *     - input channel count is q.
 *     - per output channel filter weights are Q = ( Q1, Q2, ..., Qq ).
 *     - per output channel bias weights are R.
 *     - activation escaping is ( scale = A, translate = B )
 *     - per channel .beforeActivation is X = ( x1, x2, ..., xq ) = W * Q + R.
 *     - per channel .output is ( AX + B )
 *
 *   - This depthwise
 *     - filter size is s.
 *     - per output channel filter weights are S = ( S1, S2, ..., Ss ).
 *     - per output channel bias weights are T.
 *     - activation escaping is ( scale = C, translate = D )
 *     - per channel .beforeActivation is Y = ( y1, y2, ..., ys ) = ( AX + B ) * S + T.
 *     - per channel .output is ( CY + D )
 *
 *   - This pointwise2 (always has bias, always has no activation)
 *     - input channel count is u.
 *     - per input channel extra scale-translate is ( scale = E, translate = F )
 *     - per output channel filter weights are U = ( U1, U2, ..., Uu ).
 *     - per output channel bias weights are F + V.
 *     - per channel .output is Z = ( z1, z2, ..., zu ) = ( CY + D ) * ( E * U ) + ( F + V ).
 *
 * Find out the extra scale-translate ( E, F ) so that Z = ( ( W * Q + R ) * S + T ) * U + V
 *   Z = ( CY + D ) * ( E * U ) + ( F + V )
 *     = ( C * ( ( AX + B ) * S + T ) + D ) * ( E * U ) + ( F + V )
 *     = ( C * ( ( A * ( W * Q + R ) + B ) * S + T ) + D ) * ( E * U ) + ( F + V )
 *     =
 *
 *
 *
 *

//!!!
 *   - This pointwise (or depthwise) filter weights are U = ( U1, U2, ... Um ).
 *   - This pointwise (or depthwise) filter weights are U = ( U1, U2, ... Um ).
 *   - This pointwise input channel count is m. (Or, this depthwise filter size is m.)
 *   - This pointwise (or depthwise) filter weights are U = ( U1, U2, ... Um ).
 *   - This pointwise (or depthwise) bias weights are V.
 *
 * This pointwise (or depthwise) .beforeActivation will be:
 *   Y = ( AX + B ) * U + V
 *     = ( ( Ax1 + B ) * U1 ) + ( ( Ax2 + B ) * U2 ) + ... + ( ( Axm + B ) * Um ) + V
 *     = ( AU1x1 + BU1 ) + ( AU2x2 + BU2 ) + ... + ( AUmxm + BUm ) + V
 *     = ( AU1x1 + AU2x2 + ... + AUmxm ) + ( BU1 + BU2 + ... + BUm ) + V
 *     = A * ( U1x1 + U2x2 + ... + Umxm ) + B * ( U1 + U2 + ... + Um ) + V
 *
 * Suppose
 *   - This pointwise (or depthwise) activation escaping is ( scale = C, translate = D )
 *
 *
 * This pointwise (or depthwise) .output will be:
 *   Z = CY + D
 *     = C * ( A * ( U1x1 + U2x2 + ... + Umxm ) + B * ( U1 + U2 + ... + Um ) + V ) + D
 *     = AC * ( U1x1 + U2x2 + ... + Umxm ) + C * ( B * ( U1 + U2 + ... + Um ) + V ) + D
 *
 * How to get back ( UX + V ) from Z?
 *   ( UX + V ) = ( U1x1 + U2x2 + ... + Umxm ) + V
 *
 * Let:
 *   - undo.scale = 1 / AC
 *   - undo.translate = V - ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale
 *
 * Apply ( undo.scale, undo.translate ) to Z:
 *   ( Z * undo.scale ) + undo.translate
 *   = ( AC * ( U1x1 + U2x2 + ... + Umxm ) + C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale + undo.translate
 *   = AC * ( U1x1 + U2x2 + ... + Umxm ) * undo.scale + ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale + undo.translate
 *   = AC * ( U1x1 + U2x2 + ... + Umxm ) / AC + ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale + undo.translate
 *   = ( U1x1 + U2x2 + ... + Umxm ) + ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale + undo.translate
 *   = ( U1x1 + U2x2 + ... + Umxm ) + ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale + V
 *                                  - ( C * ( B * ( U1 + U2 + ... + Um ) + V ) + D ) * undo.scale
 *   = ( U1x1 + U2x2 + ... + Umxm ) + V
 *   = ( UX + V )
 *

//!!! (2022/01/05 Remarked) Old Wrong!
//  *   - undo.scale = 1 / ( AU1 + AU2 + ... + AUm )
//  *   - undo.translate = D - ( C * ( BU1 + BU2 + ... + BUm + V ) / ( AU1 + AU2 + ... + AUm ) ) - ( D / ( AU1 + AU2 + ... + AUm ) )
//  *                    = D - ( C * ( BU1 + BU2 + ... + BUm + V ) * undo.scale ) - ( D * undo.scale )
//  *                    = D - ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale
//  *
//  * Apply ( undo.scale, undo.translate ) to Z:
//  *   ( Z * undo.scale ) + undo.translate
//  *   = ( C * ( AU1 + AU2 + ... + AUm ) * x + ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) ) * undo.scale + undo.translate
//  *   = ( C * ( AU1 + AU2 + ... + AUm ) * x * undo.scale ) + ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale ) + undo.translate
//  *   = ( C * ( AU1 + AU2 + ... + AUm ) * x / ( AU1 + AU2 + ... + AUm ) ) + ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale ) + undo.translate
//  *   = Cx + ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale ) + undo.translate
//  *   = Cx + ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale ) + ( D - ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale )
//  *   = Cx + ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale ) + D
//  *        - ( ( C * ( BU1 + BU2 + ... + BUm + V ) + D ) * undo.scale )
//  *   = Cx + D
 *
 *
 * 2. What if this pointwise (or depthwise) does not have filter weights ( U1, U2, ... Um )? (e.g avg/max pooling)
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 * 3. What if this pointwise (or depthwise) does not have bias weights V?
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
<|endoftext|>


<commit_before>/**
 * @file This file is an importable module to handle neural (web) worker body by worker proxy.
 *
 */

import * as WorkerProxy from "./WorkerProxy.js";
//import * as ValueMax from "../ValueMax.js";
//import * as Net from "./Net.js";

export { Base };

/**
 * The container of WorkerProxy. It orchestrates these WorkerProxy. Especially, it transfers (scaled) source image data to and from
 * web worker. This could maximize parallel computing under the restriction transferring source image data to every web worker serially.
 *
 * Every worker handles one neural network. When processTensor() is called, the input (usually a large memory block)
 * will be transffered to the 1st worker to start computing, and then transffered to the 2nd worker to start computing, ... etc.
 *
 * When passing large data by Worker.postMessage(), it is preferred by transferring (not by copying). If the large data wants to be
 * transferred (not copied) to many workers, the only possible way is to transferring them serially.
 *
 * However, serially transferring hurts the performance. Workers are better to compute parallelly. So every worker should transfer the
 * (possible scaled) source image data back to this WorkerController, and keep computing neural network at the same. And then, this
 * WorkerController will transfer the source image data to the next worker as soon as possible.
 *
 * Finally, this WorkerController collects all web workers' processTensor() results in a promise. The promise will resolve with an array
 * of typed-array. Every typed-array is the output of one neural network.
 */
class Base {

  /**
   * Initialize this worker controller. It will create two web workers and inform them to create a neural network per worker.
   *
   * @param {string} tensorflowJsURL
   *   The URL of tensorflow javascript library. Every worker will load the library from the URL.
   *
   * @param {Net.Config} neuralNetConfig
   *   The configuration of the neural network which will be created by this web worker.
   *
   * @param {string} weightsURL
   *   The URL of neural network weights. Every worker will load weights from the URL to initialize one neural network.
   *
!!! game_object_id_array.length as totalWorkerCount.
!!! Handle game object created and destroyed. Create and destroy web worker in needed.

   * @param {number[]} game_object_id_array
   *   The id for game objects.
   */
  init(
      tensorflowJsURL,
      neuralNetConfig, totalWorkerCount, weightsURL
//      game_object_id_array
  ) {

    this.tensorflowJsURL = tensorflowJsURL;
    this.neuralNetConfig = neuralNetConfig;
    this.weightsURL = weightsURL;

    this.processingId = -1; // The current processing id. Negative means processTensor() has not been called. Every processTensor() call will use a new id.

    this.hardwareConcurrency = navigator.hardwareConcurrency; // logical CPU count.

//!!! ...unfinished... According to logical CPU count, create so many web worker.
//!!! ...unfinished... Perhaps, two web workers are sufficient.
    // Our neural networks are learning by differential evolution. Differential evolution evaluates just two entities every time.
    // The output of one neural network will contain action signals of all game obejcts. However, only half of the output will be used
    // because one neural network only control one alignment of the game world.

    this.initProgressAll = new WorkerProxy.InitProgressAll(); // Statistics of progress of all workers' initialization.

    this.workerProxyArray = new Array( totalWorkerCount );
    for ( let i = 0; i < totalWorkerCount; ++i ) {
      let initProgress = this.initProgressAll.childrren[ i ];
      let workerProxy = this.workerProxyArray[ i ] = new WorkerProxy.Base();
      workerProxy.init( i, tensorflowJsURL, neuralNetConfig, weightsURL, initProgress );
    }

    this.processTensorPromiseArray = new Array( totalWorkerCount ); // Pre-allocation for reducing re-allocation.
  }

  /**
   *
   */
  disposeWorkers() {
    if ( this.workerProxyArray ) {
      for ( let i = 0; i < this.workerProxyArray.length; ++i ) {
        this.workerProxyArray[ i ].disposeWorker();
      }
      this.workerProxyArray = null;
    }

    this.initProgressAll = null;
  }

  /**
   * @param {ImageData} sourceImageData
   *   The source image data to be processed. Its shape [ height, width, channel ] should be the same as the size which is used when
   * training the neural network.
   *
   * @return {Promise}
   *   Return a promise which will be resolved when all worker processing promises of the same processingId are resolved. The promise
   * resolves with an array of typed-array. Every typed-array comes from the output tensor of one worker's neural network.
   */
  async processImageDataAsync( sourceImageData ) {

    let processingId = ++this.processingId; // Generate a new processing id so that the result returned from worker could be distinguished.

    let workerProxy, processRelayPromises, sourceTypedArray;

    // For first (i == 0) web worker, passing source ImageData.
    workerProxy = this.workerProxyArray[ 0 ];
    this.processTensorPromiseArray[ 0 ] = workerProxy.processImageDataAsync( processingId, sourceImageData );
    // Now, sourceImageData.data.buffer has become invalid because it is transferred (not copied) to the above web worker.

    // For all other (i >= 1) web workers, passing scaled source typed-array.
    for ( let i = 1; i < this.workerProxyArray.length; ++i ) {

      // Wait the previous web worker transferring back the scaled source typed-array. (after it has been copied into a new tensor inside the web worker).
      processRelayPromises = workerProxy.processRelayPromisesMap.get( processingId );
      sourceTypedArray = await processRelayPromises.relay.promise;

      workerProxy = this.workerProxyArray[ i ];
      this.processTensorPromiseArray[ i ] = workerProxy.processTypedArrayAsync( processingId, sourceTypedArray );
      // Now, sourceTypedArray.buffer has become invalid because it is transferred (not copied) to the above web worker.
    }

    // Note: Array push() is faster than unshift(), and unshift() is faster than concat().

    // Since all web worker has received the source image data (although serially), wait for all them done.
    let promiseAllSettled = Promise.allSettled( this.processTensorPromiseArray );
    return promiseAllSettled;
  }

}
<commit_msg>Update WorkerController.js<commit_after>/**
 * @file This file is an importable module to handle neural (web) worker body by worker proxy.
 *
 */

import * as WorkerProxy from "./WorkerProxy.js";
//import * as ValueMax from "../ValueMax.js";
//import * as Net from "./Net.js";

export { Base };

/**
 * The container of WorkerProxy. It orchestrates these WorkerProxy. Especially, it transfers (scaled) source image data to and from
 * web worker. This could maximize parallel computing under the restriction transferring source image data to every web worker serially.
 *
 * Every worker handles one neural network. When processTensor() is called, the input (usually a large memory block)
 * will be transffered to the 1st worker to start computing, and then transffered to the 2nd worker to start computing, ... etc.
 *
 * When passing large data by Worker.postMessage(), it is preferred by transferring (not by copying). If the large data wants to be
 * transferred (not copied) to many workers, the only possible way is to transferring them serially.
 *
 * However, serially transferring hurts the performance. Workers are better to compute parallelly. So every worker should transfer the
 * (possible scaled) source image data back to this WorkerController, and keep computing neural network at the same. And then, this
 * WorkerController will transfer the source image data to the next worker as soon as possible.
 *
 * Finally, this WorkerController collects all web workers' processTensor() results in a promise. The promise will resolve with an array
 * of typed-array. Every typed-array is the output of one neural network.
 */
class Base {

  /**
   * Initialize this worker controller. It will create two web workers and inform them to create a neural network per worker.
   *
   * @param {string} tensorflowJsURL
   *   The URL of tensorflow javascript library. Every worker will load the library from the URL.
   *
   * @param {Net.Config} neuralNetConfig
   *   The configuration of the neural network which will be created by this web worker.
   *
   * @param {string} weightsURL
   *   The URL of neural network weights. Every worker will load weights from the URL to initialize one neural network.
   *
!!! game_object_id_array.length as totalWorkerCount.
!!! Handle game object created and destroyed. Create and destroy web worker in needed.

   * @param {number[]} game_object_id_array
   *   The id for game objects.
   */
  init(
      tensorflowJsURL,
      neuralNetConfig, totalWorkerCount, weightsURL
//      game_object_id_array
  ) {

    this.tensorflowJsURL = tensorflowJsURL;
    this.neuralNetConfig = neuralNetConfig;
    this.weightsURL = weightsURL;

    this.processingId = -1; // The current processing id. Negative means processTensor() has not been called. Every processTensor() call will use a new id.

    this.hardwareConcurrency = navigator.hardwareConcurrency; // logical CPU count.

//!!! ...unfinished... According to logical CPU count, create so many web worker.
//!!! ...unfinished... Perhaps, two web workers are sufficient.
    // Our neural networks are learning by differential evolution. Differential evolution evaluates just two entities every time.
    // The output of one neural network will contain action signals of all game obejcts. However, only half of the output will be used
    // because one neural network only control one alignment of the game world.

    this.initProgressAll = new WorkerProxy.InitProgressAll(); // Statistics of progress of all workers' initialization.

    this.workerProxyArray = new Array( totalWorkerCount );
    for ( let i = 0; i < totalWorkerCount; ++i ) {
      let initProgress = this.initProgressAll.childrren[ i ];
      let workerProxy = this.workerProxyArray[ i ] = new WorkerProxy.Base();
      workerProxy.init( i, tensorflowJsURL, neuralNetConfig, weightsURL, initProgress );
    }

    this.processTensorPromiseArray = new Array( totalWorkerCount ); // Pre-allocation for reducing re-allocation.
  }

  /**
   *
   */
  disposeWorkers() {
    if ( this.workerProxyArray ) {
      for ( let i = 0; i < this.workerProxyArray.length; ++i ) {
        this.workerProxyArray[ i ].disposeWorker();
      }
      this.workerProxyArray = null;
    }

    this.initProgressAll = null;
  }

  /**
   * Download image data from source canvas. Pass source image data to every web worker serially by transferring.
   *
   * @param {HTMLCanvasElement} sourceCanvas
   *   The source canvas to be processed. Its shape [ height, width, channel ] should be the same as the size which is used when
   * training the neural network.
   *
   * @return {Promise}
   *   Return a promise which will be resolved when all worker processing promises of the same processingId are resolved. The promise
   * resolves with an array of typed-array. Every typed-array comes from the output tensor of one worker's neural network.
   */
  async processCanvas_Async_ByTransfer( sourceCanvas ) {
    let ctx = sourceCanvas.getContext( '2d' );
    let sourceImageData = ctx.getImageData( 0, 0, sourceCanvas.width, sourceCanvas.height );
    return this.processImageData_Async_ByTransfer( sourceImageData );
  }

  /**
   * Pass source image data to every web worker serially by transferring.
   *
   * @param {ImageData} sourceImageData
   *   The source image data to be processed. Its shape [ height, width, channel ] should be the same as the size which is used when
   * training the neural network.
   *
   * @return {Promise}
   *   Return a promise which will be resolved when all worker processing promises of the same processingId are resolved. The promise
   * resolves with an array of typed-array. Every typed-array comes from the output tensor of one worker's neural network.
   */
  async processImageData_Async_ByTransfer( sourceImageData ) {

    let processingId = ++this.processingId; // Generate a new processing id so that the result returned from worker could be distinguished.

    let workerProxy, processRelayPromises, sourceTypedArray;

    // For first (i == 0) web worker, passing source ImageData.
    workerProxy = this.workerProxyArray[ 0 ];
    this.processTensorPromiseArray[ 0 ] = workerProxy.processImageDataAsync( processingId, sourceImageData );
    // Now, sourceImageData.data.buffer has become invalid because it is transferred (not copied) to the above web worker.

    // For all other (i >= 1) web workers, passing scaled source typed-array.
    for ( let i = 1; i < this.workerProxyArray.length; ++i ) {

      // Wait the previous web worker transferring back the scaled source typed-array. (after it has been copied into a new tensor inside the web worker).
      processRelayPromises = workerProxy.processRelayPromisesMap.get( processingId );
      sourceTypedArray = await processRelayPromises.relay.promise;

      workerProxy = this.workerProxyArray[ i ];
      this.processTensorPromiseArray[ i ] = workerProxy.processTypedArrayAsync( processingId, sourceTypedArray );
      // Now, sourceTypedArray.buffer has become invalid because it is transferred (not copied) to the above web worker.
    }

    // Note: Array push() is faster than unshift(), and unshift() is faster than concat().

    // Since all web worker has received the source image data (although serially), wait for all them done.
    let promiseAllSettled = Promise.allSettled( this.processTensorPromiseArray );
    return promiseAllSettled;
  }

  /**
   * Download image data from source canvas. Pass source image data to every web worker parallelly by copying.
   *
   * @param {HTMLCanvasElement} sourceCanvas
   *   The source canvas to be processed. Its shape [ height, width, channel ] should be the same as the size which is used when
   * training the neural network.
   *
   * @return {Promise}
   *   Return a promise which will be resolved when all worker processing promises of the same processingId are resolved. The promise
   * resolves with an array of typed-array. Every typed-array comes from the output tensor of one worker's neural network.
   */
  async processCanvas_Async_ByCopy( sourceCanvas ) {

    // Create (scaled) source tensor so that every web worker needs not scale again and easier to re-create source tensor.
    //
    // The drawback is that here should have tensorflow.js library loaded.
    let scaledSourceTensor = this.neuralNetConfig.create_ScaledSourceTensor_from_ImageData_or_Canvas( sourceCanvas );
    let scaledSourceTypedArray = await scaledSourceTensor.data();
    scaledSourceTensor.dispose(); // Discard the source tensor because type-array (not tensor) will be past to web worker

//!!! ...unfinished...
    let processingId = ++this.processingId; // Generate a new processing id so that the result returned from worker could be distinguished.

//!!! ...unfinished...

    let workerProxy, processRelayPromises, sourceTypedArray;

    // For first (i == 0) web worker, passing source ImageData.
    workerProxy = this.workerProxyArray[ 0 ];
    this.processTensorPromiseArray[ 0 ] = workerProxy.processImageDataAsync( processingId, sourceImageData );
    // Now, sourceImageData.data.buffer has become invalid because it is transferred (not copied) to the above web worker.

    // For all other (i >= 1) web workers, passing scaled source typed-array.
    for ( let i = 1; i < this.workerProxyArray.length; ++i ) {

      // Wait the previous web worker transferring back the scaled source typed-array. (after it has been copied into a new tensor inside the web worker).
      processRelayPromises = workerProxy.processRelayPromisesMap.get( processingId );
      sourceTypedArray = await processRelayPromises.relay.promise;

      workerProxy = this.workerProxyArray[ i ];
      this.processTensorPromiseArray[ i ] = workerProxy.processTypedArrayAsync( processingId, sourceTypedArray );
      // Now, sourceTypedArray.buffer has become invalid because it is transferred (not copied) to the above web worker.
    }

    // Note: Array push() is faster than unshift(), and unshift() is faster than concat().

    // Since all web worker has received the source image data (although serially), wait for all them done.
    let promiseAllSettled = Promise.allSettled( this.processTensorPromiseArray );
    return promiseAllSettled;
  }

}
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name dormManagementToolApp.controller:GarbageCtrl
 * @description
 * # GarbageCtrl
 * Controller of the dormManagementToolApp
 */
angular.module('dormManagementToolApp')
  .controller('GarbageCtrl', ['$http', function ($http) {
    this.bags = [
      {
        "name": "purple",
        "status": "full",
        "responsible": "Bilbo"
      },
      {
        "name": "yellow",
        "status": "ok",
        "responsible": "Pippin"
      },
      {
        "name": "indigo",
        "status": "full",
        "responsible": "Merry"
      }
    ];
    var mv = this;
    this.getGarbageBags = function () {
      $http({method: 'GET', url: 'http://localhost:3000/garbage_bags.json'})
        .then(function (response) {
          mv.bags = response.data;
        });
    };
    this.getGarbageBags();
    this.setStatus = function (id, status) {
      var body = JSON.stringify({"garbage_bag": {"status": status}});
      var url = 'http://localhost:3000/garbage_bags/' + id + '.json';
      $http({method: 'PATCH', url: url, data: body}).then(function (response) {
        //ok!
      },
      function () {
        console.log('failed to set garbage status');
      });
    };
  }]);
<commit_msg>Add authorisation header<commit_after>'use strict';

/**
 * @ngdoc function
 * @name dormManagementToolApp.controller:GarbageCtrl
 * @description
 * # GarbageCtrl
 * Controller of the dormManagementToolApp
 */
angular.module('dormManagementToolApp')
  .run(function($http) {
    if (localStorage.getItem('user')) {
      $http.defaults.headers.common.Authorization = 'Bearer ' + JSON.parse(localStorage.getItem('user')).access_token;
    }
  })
  .controller('GarbageCtrl', ['$http', function ($http) {
    this.bags = [
      {
        "name": "purple",
        "status": "full",
        "responsible": "Bilbo"
      },
      {
        "name": "yellow",
        "status": "ok",
        "responsible": "Pippin"
      },
      {
        "name": "indigo",
        "status": "full",
        "responsible": "Merry"
      }
    ];
    var mv = this;
    this.getGarbageBags = function () {
      $http({method: 'GET', url: 'http://localhost:3000/garbage_bags.json'})
        .then(function (response) {
          mv.bags = response.data;
        });
    };
    this.getGarbageBags();
    this.setStatus = function (id, status) {
      var body = JSON.stringify({"garbage_bag": {"status": status}});
      var url = 'http://localhost:3000/garbage_bags/' + id + '.json';
      $http({method: 'PATCH', url: url, data: body}).then(function (response) {
        //ok!
      },
      function () {
        console.log('failed to set garbage status');
      });
    };
  }]);
<|endoftext|>


<commit_before>import * as TestFilters2D from "./TestFilters2D.js";
//import * as TensorTools from "../util/TensorTools.js";

export { Base };

/**
 * A test set.
 */
class Base {

  /**
   * @param {number} height      image height
   * @param {number} width       image width
   * @param {number} depth       image channel count
   */
  constructor( height, width, depth ) {

    this.height = height;
    this.width = width;
    this.depth = depth;

    this.valueCount = height * width * depth;

    //this.concatenatedShape = [ height, width, depth ];

    // About half, but the difference should be divisable by 2.
//    this.targetSize = [ Math.floor( height / 20 ) * 10, Math.floor( width / 20 ) * 10 ];
    this.targetSize = [ 1, 1 ]; // to 1x1.
    let targetHeight = this.targetSize[ 0 ];

    // The filter size for achieving target size in one step.
    let filterHeight_OneStep =      ( height - this.targetSize[ 0 ] ) + 1;
    let filterWidth_OneStep =       ( width - this.targetSize[ 1 ] ) + 1;

    this.dataTensor3d = tf.tidy( () => {
      let dataTensor1d = tf.linspace( 0, this.valueCount - 1, this.valueCount );
      let dataTensor3d = dataTensor1d.reshape( [ height, width, depth ] );
      return dataTensor3d;
    });

    let inChannels =        depth;
    let channelMultiplier = 1;

    // [ TestFiltersName, sourceHeight, sourceDepth, targetHeight, filterHeight, strAvgMaxConv, bPointwise ]
    this.testFiltersSpecTable = [
      [ "DAvg_101x101_OneStep",      height, depth, targetHeight, filterHeight_OneStep,  "Avg", false ],
      [ "DAvg_101x101_1x1_OneStep",  height, depth, targetHeight, filterHeight_OneStep,  "Avg",  true ],
      [ "DMax_101x101_OneStep",      height, depth, targetHeight, filterHeight_OneStep,  "Max", false ],
      [ "DConv_101x101_OneStep",     height, depth, targetHeight, filterHeight_OneStep, "Conv", false ],
      [ "DConv_101x101_1x1_OneStep", height, depth, targetHeight, filterHeight_OneStep, "Conv",  true ],

      [ "DAvg_2x2_MultiStep",        height, depth, targetHeight,                    2,  "Avg", false ],
      [ "DMax_2x2_MultiStep",        height, depth, targetHeight,                    2,  "Max", false ],
      [ "DConv_2x2_MultiStep",       height, depth, targetHeight,                    2, "Conv", false ],
      [ "DConv_2x2_1x1_MultiStep",   height, depth, targetHeight,                    2, "Conv",  true ],

      [ "DConv_3x3_MultiStep",       height, depth, targetHeight,                    3, "Conv", false ],
      [ "DConv_6x6_MultiStep",       height, depth, targetHeight,                    6, "Conv", false ],
      [ "DConv_11x11_MultiStep",     height, depth, targetHeight,                   11, "Conv", false ],
    ];

    // Create test filters.
    this.testFiltersArray = this.testFilterSpecTable.map( ( filtersSpec, i ) => {
      let testFilters = new TestFilters2D.Base();
      testFilters.init( ...filtersSpec );
      return testFilters;
    });
  }

  disposeTensors() {
    if ( this.dataTensor3d ) {
      tf.dispose( this.dataTensor3d );
      this.dataTensor3d = null;
    }

    // Release test filters.
    this.testFiltersArray.forEach( ( testFilters, i ) => {
      testFilters.disposeTensors();
    });
    this.testFiltersArray = null;
    this.testFiltersSpecTable = null;
  }

  // Test depthwise convolution (2D) by 3x3 filter with stride 2
  test_DepthwiseConv2d_3x3_Stride2( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.depthwiseConv2d(
                this.depthwiseConv_3x3.depthwiseConvFiltersTensor4dArray[ 0 ], 2, "same" );
      if ( bReturn )
        return t;
    });
  }

//!!! ...unfinished...
// resize 1/2 v.s. tf.pool( windowShape = [ 2, 2 ], poolingType = “avg”, pad = “valid”, dilations = 1, strides = 2 )
  // Test rsize-bilinear
  test_ResizeBilinear( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.resizeBilinear( this.targetSize, true );
      if ( bReturn )
        return t;
    });
  }

  // Test rsize-nearest-neighbor
  test_ResizeNearestNeighbor( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.resizeNearestNeighbor( this.targetSize, true );
      if ( bReturn )
        return t;
    });
  }

  // Testing whether the results of different implementation are the same.
  async testResultSame() {
    tf.tidy( () => {

//      let quarterTensor_3x3_Stride2 =             this.test_DepthwiseConv2d_3x3_Stride2( true );
      let quarterTensor_ResizeNearestNeighbor =   this.test_ResizeNearestNeighbor( true );
      let quarterTensor_ResizeBilinear =          this.test_ResizeBilinear( true );

//       tf.util.assert(
//         tf.util.arraysEqual( quarterTensor_11x11.shape, quarterTensor_3x3_Stride2.shape ),
//         `DepthwiseConv2d_11x11_MultiStep() != DepthwiseConv2d_3x3_Stride2()`);
//
//       tf.util.assert(
//         tf.util.arraysEqual( quarterTensor_3x3_Stride2.shape, quarterTensor_ResizeBilinear.shape ),
//         `DepthwiseConv2d_3x3_Stride2() != ResizeBilinear()`);

      tf.util.assert(
        tf.util.arraysEqual( quarterTensor_ResizeBilinear.shape, quarterTensor_ResizeNearestNeighbor.shape ),
        `ResizeBilinear() != ResizeNearestNeighbor()`);

      // All test filters should generate same size result.
      this.testFiltersArray.forEach( ( testFilters, i ) => {
        let t = testFilters.apply( this.dataTensor3d, true );

        tf.util.assert(
          tf.util.arraysEqual( t.shape, quarterTensor_ResizeBilinear.shape ),
          `${testFilters.name}() != ResizeBilinear()`);

        t.dispose();
      });

    });

    // The above codes also compiles the codes.
    // Since the codes compiled, their execute time can be tested now.

    await this.logProfile( "ResizeNearestNeighbor", this.test_ResizeNearestNeighbor.bind( this ) );
    await this.logProfile( "ResizeBilinear", this.test_ResizeBilinear.bind( this ) );

    // All test filters should generate same size result.
    this.testFiltersArray.forEach( ( testFilters, i ) => {
      // Note: Do not return result tensor so that need not to dispose them.
      await this.logProfile( testFilters.name, testFilters.apply.bind( this.dataTensor3d, false ) );
    });
  }

  async logProfile( title, func ) {

    // Get backend name before the following promise. Otherwise, it may be changed when the function been executed.
    let backendName = tf.getBackend();

    let profile = await tf.profile( func );
    let time = await tf.time( func );

    console.log(
       `${title} (${backendName}): `

     + `newBytes: ${profile.newBytes}, `
     + `newTensors: ${profile.newTensors}, `
     + `peakBytes: ${profile.peakBytes}, `
//     + `byte usage over all kernels: ${profile.kernels.map(k => k.totalBytesSnapshot)}, `

     + `kernelMs: ${time.kernelMs}, wallTimeMs: ${time.wallMs}`);
  }
}
<commit_msg>Update HeightWidthDepth.js<commit_after>import * as TestFilters2D from "./TestFilters2D.js";
//import * as TensorTools from "../util/TensorTools.js";

export { Base };

/**
 * A test set.
 */
class Base {

  /**
   * @param {number} height      image height
   * @param {number} width       image width
   * @param {number} depth       image channel count
   */
  constructor( height, width, depth ) {

    this.height = height;
    this.width = width;
    this.depth = depth;

    this.valueCount = height * width * depth;

    //this.concatenatedShape = [ height, width, depth ];

    // About half, but the difference should be divisable by 2.
//    this.targetSize = [ Math.floor( height / 20 ) * 10, Math.floor( width / 20 ) * 10 ];
    this.targetSize = [ 1, 1 ]; // to 1x1.
    let targetHeight = this.targetSize[ 0 ];

    // The filter size for achieving target size in one step.
    let filterHeight_OneStep =      ( height - this.targetSize[ 0 ] ) + 1;
    let filterWidth_OneStep =       ( width - this.targetSize[ 1 ] ) + 1;

    this.dataTensor3d = tf.tidy( () => {
      let dataTensor1d = tf.linspace( 0, this.valueCount - 1, this.valueCount );
      let dataTensor3d = dataTensor1d.reshape( [ height, width, depth ] );
      return dataTensor3d;
    });

    let inChannels =        depth;
    let channelMultiplier = 1;

    // [ TestFiltersName, sourceHeight, sourceDepth, targetHeight, filterHeight, strAvgMaxConv, bPointwise ]
    this.testFiltersSpecTable = [
      [ "DAvg_101x101_OneStep",      height, depth, targetHeight, filterHeight_OneStep,  "Avg", false ],
      [ "DAvg_101x101_1x1_OneStep",  height, depth, targetHeight, filterHeight_OneStep,  "Avg",  true ],
      [ "DMax_101x101_OneStep",      height, depth, targetHeight, filterHeight_OneStep,  "Max", false ],
      [ "DConv_101x101_OneStep",     height, depth, targetHeight, filterHeight_OneStep, "Conv", false ],
      [ "DConv_101x101_1x1_OneStep", height, depth, targetHeight, filterHeight_OneStep, "Conv",  true ],

      [ "DAvg_2x2_MultiStep",        height, depth, targetHeight,                    2,  "Avg", false ],
      [ "DMax_2x2_MultiStep",        height, depth, targetHeight,                    2,  "Max", false ],
      [ "DConv_2x2_MultiStep",       height, depth, targetHeight,                    2, "Conv", false ],
      [ "DConv_2x2_1x1_MultiStep",   height, depth, targetHeight,                    2, "Conv",  true ],

      [ "DConv_3x3_MultiStep",       height, depth, targetHeight,                    3, "Conv", false ],
      [ "DConv_6x6_MultiStep",       height, depth, targetHeight,                    6, "Conv", false ],
      [ "DConv_11x11_MultiStep",     height, depth, targetHeight,                   11, "Conv", false ],
    ];

    // Create test filters.
    this.testFiltersArray = this.testFilterSpecTable.map( ( filtersSpec, i ) => {
      let testFilters = new TestFilters2D.Base();
      testFilters.init( ...filtersSpec );
      return testFilters;
    });
  }

  disposeTensors() {
    if ( this.dataTensor3d ) {
      tf.dispose( this.dataTensor3d );
      this.dataTensor3d = null;
    }

    // Release test filters.
    this.testFiltersArray.forEach( ( testFilters, i ) => {
      testFilters.disposeTensors();
    });
    this.testFiltersArray = null;
    this.testFiltersSpecTable = null;
  }

  // Test depthwise convolution (2D) by 3x3 filter with stride 2
  test_DepthwiseConv2d_3x3_Stride2( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.depthwiseConv2d(
                this.depthwiseConv_3x3.depthwiseConvFiltersTensor4dArray[ 0 ], 2, "same" );
      if ( bReturn )
        return t;
    });
  }

//!!! ...unfinished...
// resize 1/2 v.s. tf.pool( windowShape = [ 2, 2 ], poolingType = “avg”, pad = “valid”, dilations = 1, strides = 2 )
  // Test rsize-bilinear
  test_ResizeBilinear( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.resizeBilinear( this.targetSize, true );
      if ( bReturn )
        return t;
    });
  }

  // Test rsize-nearest-neighbor
  test_ResizeNearestNeighbor( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.resizeNearestNeighbor( this.targetSize, true );
      if ( bReturn )
        return t;
    });
  }

  // Testing whether the results of different implementation are the same.
  async testResultSame() {
    tf.tidy( () => {

//      let quarterTensor_3x3_Stride2 =             this.test_DepthwiseConv2d_3x3_Stride2( true );
      let quarterTensor_ResizeNearestNeighbor =   this.test_ResizeNearestNeighbor( true );
      let quarterTensor_ResizeBilinear =          this.test_ResizeBilinear( true );

//       tf.util.assert(
//         tf.util.arraysEqual( quarterTensor_11x11.shape, quarterTensor_3x3_Stride2.shape ),
//         `DepthwiseConv2d_11x11_MultiStep() != DepthwiseConv2d_3x3_Stride2()`);
//
//       tf.util.assert(
//         tf.util.arraysEqual( quarterTensor_3x3_Stride2.shape, quarterTensor_ResizeBilinear.shape ),
//         `DepthwiseConv2d_3x3_Stride2() != ResizeBilinear()`);

      tf.util.assert(
        tf.util.arraysEqual( quarterTensor_ResizeBilinear.shape, quarterTensor_ResizeNearestNeighbor.shape ),
        `ResizeBilinear() != ResizeNearestNeighbor()`);

      // All test filters should generate same size result.
      this.testFiltersArray.forEach( ( testFilters, i ) => {
        let t = testFilters.apply( this.dataTensor3d, true );

        tf.util.assert(
          tf.util.arraysEqual( t.shape, quarterTensor_ResizeBilinear.shape ),
          `${testFilters.name}() != ResizeBilinear()`);

        t.dispose();
      });

    });

    // The above codes also compiles the codes.
    // Since the codes compiled, their execute time can be tested now.

    await this.logProfile( "ResizeNearestNeighbor", this.test_ResizeNearestNeighbor.bind( this ) );
    await this.logProfile( "ResizeBilinear", this.test_ResizeBilinear.bind( this ) );

    // All test filters should generate same size result.
//     this.testFiltersArray.forEach( ( testFilters, i ) => {
//       // Note: Do not return result tensor so that need not to dispose them.
//       await this.logProfile( testFilters.name, testFilters.apply.bind( this.dataTensor3d, false ) );
//     });
    for ( let testFilters of this.testFiltersArray ) {
      // Note: Do not return result tensor so that need not to dispose them.
      await this.logProfile( testFilters.name, testFilters.apply.bind( this.dataTensor3d, false ) );
    }
  }

  async logProfile( title, func ) {

    // Get backend name before the following promise. Otherwise, it may be changed when the function been executed.
    let backendName = tf.getBackend();

    let profile = await tf.profile( func );
    let time = await tf.time( func );

    console.log(
       `${title} (${backendName}): `

     + `newBytes: ${profile.newBytes}, `
     + `newTensors: ${profile.newTensors}, `
     + `peakBytes: ${profile.peakBytes}, `
//     + `byte usage over all kernels: ${profile.kernels.map(k => k.totalBytesSnapshot)}, `

     + `kernelMs: ${time.kernelMs}, wallTimeMs: ${time.wallMs}`);
  }
}
<|endoftext|>


<commit_before>import * as TestFilters2D from "./TestFilters2D.js";
//import * as TensorTools from "../util/TensorTools.js";

export { Base };

/**
 * A test set.
 */
class Base {

  /**
   * @param {number} height      image height
   * @param {number} width       image width
   * @param {number} depth       image channel count
   *
   * @param {ValueMax.Percentage.Aggregate} progressToYield
   *   Return this when every time yield. Usually, this is the container of the progressToAdvance.
   *
   * @param {ValueMax.Percentage.Concrete}  progressToAdvance
   *   Increase this when every time advanced. It will be initialized to zero when decoder starting.
   *
   * @param {ValueMax.Receiver.Base}  progressReceiver
   *   Every time the progress is advanced, progressReceiver.setValueMax( progressToYield ) will be called.
   */
  constructor( height, width, depth, progressToYield, progressToAdvance, progressReceiver ) {

    this.height = height;
    this.width = width;
    this.depth = depth;

    this.progressToYield = progressToYield;
    this.progressToAdvance = progressToAdvance;
    this.progressReceiver = progressReceiver;

    this.valueCount = height * width * depth;

    //this.concatenatedShape = [ height, width, depth ];

    // About half, but the difference should be divisable by 2.
//    this.targetSize = [ Math.floor( height / 20 ) * 10, Math.floor( width / 20 ) * 10 ];
    this.targetSize = [ 1, 1 ]; // to 1x1.
    let targetHeight = this.targetSize[ 0 ];

    // The filter size for achieving target size in one step.
    let filterHeight_OneStep =      ( height - this.targetSize[ 0 ] ) + 1;
    let filterWidth_OneStep =       ( width - this.targetSize[ 1 ] ) + 1;

    this.dataTensor3d = tf.tidy( () => {
      let dataTensor1d = tf.linspace( 0, this.valueCount - 1, this.valueCount );
      let dataTensor3d = dataTensor1d.reshape( [ height, width, depth ] );
      return dataTensor3d;
    });

    let inChannels =        depth;
    let channelMultiplier = 1;

    // [ TestFiltersName, sourceHeight, sourceDepth, targetHeight, filterHeight, strAvgMaxConv, bPointwise ]
    this.testFiltersSpecTable = [
      [ "DAvg_101x101_OneStep",      height, depth, targetHeight, filterHeight_OneStep,  "Avg", false ],
      [ "DAvg_101x101_1x1_OneStep",  height, depth, targetHeight, filterHeight_OneStep,  "Avg",  true ],
      [ "DMax_101x101_OneStep",      height, depth, targetHeight, filterHeight_OneStep,  "Max", false ],
      [ "DConv_101x101_OneStep",     height, depth, targetHeight, filterHeight_OneStep, "Conv", false ],
      [ "DConv_101x101_1x1_OneStep", height, depth, targetHeight, filterHeight_OneStep, "Conv",  true ],

      [ "DAvg_2x2_MultiStep",        height, depth, targetHeight,                    2,  "Avg", false ],
      [ "DMax_2x2_MultiStep",        height, depth, targetHeight,                    2,  "Max", false ],
      [ "DConv_2x2_MultiStep",       height, depth, targetHeight,                    2, "Conv", false ],
      [ "DConv_2x2_1x1_MultiStep",   height, depth, targetHeight,                    2, "Conv",  true ],

      [ "DConv_3x3_MultiStep",       height, depth, targetHeight,                    3, "Conv", false ],
      [ "DConv_6x6_MultiStep",       height, depth, targetHeight,                    6, "Conv", false ],
      [ "DConv_11x11_MultiStep",     height, depth, targetHeight,                   11, "Conv", false ],
    ];

    // Create test filters.
    this.testFiltersArray = this.testFiltersSpecTable.map( ( filtersSpec, i ) => {
      let testFilters = new TestFilters2D.Base();
      testFilters.init( ...filtersSpec );
      return testFilters;
    });

    // Initialize progress accumulator.

    // "+2" for test_ResizeNearestNeighbor and test_ResizeBilinear.
    // "*2" for compiling (with assert) and profiling.
    progressToAdvance.total = ( this.testFiltersArray.length + 2 ) * 2;
    progressToAdvance.accumulation = 0;
  }

  disposeTensors() {
    if ( this.dataTensor3d ) {
      tf.dispose( this.dataTensor3d );
      this.dataTensor3d = null;
    }

    // Release test filters.
    this.testFiltersArray.forEach( ( testFilters, i ) => {
      testFilters.disposeTensors();
    });
    this.testFiltersArray = null;
    this.testFiltersSpecTable = null;
  }

  // Test depthwise convolution (2D) by 3x3 filter with stride 2
  test_DepthwiseConv2d_3x3_Stride2( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.depthwiseConv2d(
                this.depthwiseConv_3x3.depthwiseConvFiltersTensor4dArray[ 0 ], 2, "same" );
      if ( bReturn )
        return t;
    });
  }

  // Test rsize-nearest-neighbor
  test_ResizeNearestNeighbor( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.resizeNearestNeighbor( this.targetSize, true );
      if ( bReturn )
        return t;
    });
  }

//!!! ...unfinished...
// resize 1/2 v.s. tf.pool( windowShape = [ 2, 2 ], poolingType = “avg”, pad = “valid”, dilations = 1, strides = 2 )
  // Test rsize-bilinear
  test_ResizeBilinear( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.resizeBilinear( this.targetSize, true );
      if ( bReturn )
        return t;
    });
  }

  /** Advance the progressToAdvance, and report progressToYield to progressReceiver. */
  progressAdvanveReport() {
      ++this.progressToAdvance.accumulation;
      this.progressReceiver.setValueMax( this.progressToYield );
  }

  /**
   * Testing whether the results of different implementation are the same.
   *
   * @return {Object[]}
   *   Return array of profile infomation { title, backendName, newBytes, newTensors, peakBytes, kernelMs, wallMs }.
   */
  async generateProfiles() {
    let resultProfiles = [];

    tf.tidy( () => {

//      let quarterTensor_3x3_Stride2 =             this.test_DepthwiseConv2d_3x3_Stride2( true );
      let quarterTensor_ResizeNearestNeighbor =   this.test_ResizeNearestNeighbor( true );
      this.progressAdvanveReport();

      let quarterTensor_ResizeBilinear =          this.test_ResizeBilinear( true );
      this.progressAdvanveReport();

//       tf.util.assert(
//         tf.util.arraysEqual( quarterTensor_11x11.shape, quarterTensor_3x3_Stride2.shape ),
//         `DepthwiseConv2d_11x11_MultiStep() != DepthwiseConv2d_3x3_Stride2()`);
//
//       tf.util.assert(
//         tf.util.arraysEqual( quarterTensor_3x3_Stride2.shape, quarterTensor_ResizeBilinear.shape ),
//         `DepthwiseConv2d_3x3_Stride2() != ResizeBilinear()`);

      tf.util.assert(
        tf.util.arraysEqual( quarterTensor_ResizeBilinear.shape, quarterTensor_ResizeNearestNeighbor.shape ),
        `ResizeBilinear() != ResizeNearestNeighbor()`);

      // All test filters should generate same size result.
      this.testFiltersArray.forEach( ( testFilters, i ) => {
        let t = testFilters.apply( this.dataTensor3d, true );

        tf.util.assert(
          tf.util.arraysEqual( t.shape, quarterTensor_ResizeBilinear.shape ),
          `${testFilters.name}() != ResizeBilinear()`);

        t.dispose();

        this.progressAdvanveReport();
      });

    });

    // The above codes also compiles the codes.
    // Since the codes compiled, their execute time can be tested now.

    resultProfiles.push( await this.logProfile( "ResizeNearestNeighbor", this.test_ResizeNearestNeighbor.bind( this ) ) );
    this.progressAdvanveReport();

    resultProfiles.push( await this.logProfile( "ResizeBilinear", this.test_ResizeBilinear.bind( this ) ) );
    this.progressAdvanveReport();

    // All test filters in array.
    for ( let testFilters of this.testFiltersArray ) {
      // Note: Do not return result tensor so that need not to dispose them.
      resultProfiles.push(
        await this.logProfile( testFilters.name, testFilters.apply.bind( testFilters, this.dataTensor3d, false ) ) );

      this.progressAdvanveReport();
    }

    return resultProfiles;
  }

  /**
   * @return {Object}
   *   Return profile infomation { title, backendName, newBytes, newTensors, peakBytes, kernelMs, wallMs }.
   */
  async logProfile( title, func ) {

    // Get backend name before the following promise. Otherwise, it may be changed when the function been executed.
    let backendName = tf.getBackend();

    let profile = await tf.profile( func );
    let time = await tf.time( func );

    console.log(
       `${title} (${backendName}): `

     + `newBytes: ${profile.newBytes}, `
     + `newTensors: ${profile.newTensors}, `
     + `peakBytes: ${profile.peakBytes}, `
//     + `byte usage over all kernels: ${profile.kernels.map(k => k.totalBytesSnapshot)}, `

     + `kernelMs: ${time.kernelMs}, wallTimeMs: ${time.wallMs}`);

    let resultProfile = {
      title: title,
      backendName: backendName,
      newBytes: profile.newBytes,
      newTensors: profile.newTensors,
      peakBytes: profile.peakBytes,
      kernelMs: time.kernelMs,
      wallMs: time.wallMs
    };

    return resultProfile;
  }
}
<commit_msg>Update HeightWidthDepth.js<commit_after>import * as PartTime from "../PartTime.js";
import * as TestFilters2D from "./TestFilters2D.js";
//import * as TensorTools from "../util/TensorTools.js";

export { Base };

/**
 * A test set.
 */
class Base {

  /**
   * @param {number} height      image height
   * @param {number} width       image width
   * @param {number} depth       image channel count
   *
   * @param {ValueMax.Percentage.Aggregate} progressToYield
   *   Return this when every time yield. Usually, this is the container of the progressToAdvance.
   *
   * @param {ValueMax.Percentage.Concrete}  progressToAdvance
   *   Increase this when every time advanced. It will be initialized to zero when decoder starting.
   *
   * @param {ValueMax.Receiver.Base}  progressReceiver
   *   Every time the progress is advanced, progressReceiver.setValueMax( progressToYield ) will be called.
   */
  constructor( height, width, depth, progressToYield, progressToAdvance, progressReceiver ) {

    this.height = height;
    this.width = width;
    this.depth = depth;

    this.progressToYield = progressToYield;
    this.progressToAdvance = progressToAdvance;
    this.progressReceiver = progressReceiver;

    this.valueCount = height * width * depth;

    //this.concatenatedShape = [ height, width, depth ];

    // About half, but the difference should be divisable by 2.
//    this.targetSize = [ Math.floor( height / 20 ) * 10, Math.floor( width / 20 ) * 10 ];
    this.targetSize = [ 1, 1 ]; // to 1x1.
    let targetHeight = this.targetSize[ 0 ];

    // The filter size for achieving target size in one step.
    let filterHeight_OneStep =      ( height - this.targetSize[ 0 ] ) + 1;
    let filterWidth_OneStep =       ( width - this.targetSize[ 1 ] ) + 1;

    this.dataTensor3d = tf.tidy( () => {
      let dataTensor1d = tf.linspace( 0, this.valueCount - 1, this.valueCount );
      let dataTensor3d = dataTensor1d.reshape( [ height, width, depth ] );
      return dataTensor3d;
    });

    let inChannels =        depth;
    let channelMultiplier = 1;

    // [ TestFiltersName, sourceHeight, sourceDepth, targetHeight, filterHeight, strAvgMaxConv, bPointwise ]
    this.testFiltersSpecTable = [
      [ "DAvg_101x101_OneStep",      height, depth, targetHeight, filterHeight_OneStep,  "Avg", false ],
      [ "DAvg_101x101_1x1_OneStep",  height, depth, targetHeight, filterHeight_OneStep,  "Avg",  true ],
      [ "DMax_101x101_OneStep",      height, depth, targetHeight, filterHeight_OneStep,  "Max", false ],
      [ "DConv_101x101_OneStep",     height, depth, targetHeight, filterHeight_OneStep, "Conv", false ],
      [ "DConv_101x101_1x1_OneStep", height, depth, targetHeight, filterHeight_OneStep, "Conv",  true ],

      [ "DAvg_2x2_MultiStep",        height, depth, targetHeight,                    2,  "Avg", false ],
      [ "DMax_2x2_MultiStep",        height, depth, targetHeight,                    2,  "Max", false ],
      [ "DConv_2x2_MultiStep",       height, depth, targetHeight,                    2, "Conv", false ],
      [ "DConv_2x2_1x1_MultiStep",   height, depth, targetHeight,                    2, "Conv",  true ],

      [ "DConv_3x3_MultiStep",       height, depth, targetHeight,                    3, "Conv", false ],
      [ "DConv_6x6_MultiStep",       height, depth, targetHeight,                    6, "Conv", false ],
      [ "DConv_11x11_MultiStep",     height, depth, targetHeight,                   11, "Conv", false ],
    ];

    // Create test filters.
    this.testFiltersArray = this.testFiltersSpecTable.map( ( filtersSpec, i ) => {
      let testFilters = new TestFilters2D.Base();
      testFilters.init( ...filtersSpec );
      return testFilters;
    });

    // Initialize progress accumulator.

    // "+2" for test_ResizeNearestNeighbor and test_ResizeBilinear.
    // "*2" for compiling (with assert) and profiling.
    progressToAdvance.total = ( this.testFiltersArray.length + 2 ) * 2;
    progressToAdvance.accumulation = 0;
  }

  disposeTensors() {
    if ( this.dataTensor3d ) {
      tf.dispose( this.dataTensor3d );
      this.dataTensor3d = null;
    }

    // Release test filters.
    this.testFiltersArray.forEach( ( testFilters, i ) => {
      testFilters.disposeTensors();
    });
    this.testFiltersArray = null;
    this.testFiltersSpecTable = null;
  }

  // Test depthwise convolution (2D) by 3x3 filter with stride 2
  test_DepthwiseConv2d_3x3_Stride2( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.depthwiseConv2d(
                this.depthwiseConv_3x3.depthwiseConvFiltersTensor4dArray[ 0 ], 2, "same" );
      if ( bReturn )
        return t;
    });
  }

  // Test rsize-nearest-neighbor
  test_ResizeNearestNeighbor( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.resizeNearestNeighbor( this.targetSize, true );
      if ( bReturn )
        return t;
    });
  }

//!!! ...unfinished...
// resize 1/2 v.s. tf.pool( windowShape = [ 2, 2 ], poolingType = “avg”, pad = “valid”, dilations = 1, strides = 2 )
  // Test rsize-bilinear
  test_ResizeBilinear( bReturn ) {
    return tf.tidy( () => {
      let t = this.dataTensor3d.resizeBilinear( this.targetSize, true );
      if ( bReturn )
        return t;
    });
  }

  /** Advance the progressToAdvance, and report progressToYield to progressReceiver. */
  * progressAdvanceYield() {
    ++this.progressToAdvance.accumulation;
    yield this.progressToYield;
  }

  /**
   * Testing whether the results of different implementation are the same.
   *
   * @yield {ValueMax.Percentage.Aggregate or Object[]}
   *   Yield ( value = progressToYield ) when ( done = false ).
   *   Yield ( value = array of profile infomation { title, backendName, newBytes, newTensors, peakBytes, kernelMs, wallMs } ) when ( done = true ).
   */
  async * profilesGenerator() {
    let resultProfiles = [];

//    tf.tidy( () => {

//      let quarterTensor_3x3_Stride2 =             this.test_DepthwiseConv2d_3x3_Stride2( true );
      let quarterTensor_ResizeNearestNeighbor =   this.test_ResizeNearestNeighbor( true );
      yield* this.progressAdvanceYield();

      let quarterTensor_ResizeBilinear =          this.test_ResizeBilinear( true );
      yield* this.progressAdvanceYield();

//       tf.util.assert(
//         tf.util.arraysEqual( quarterTensor_11x11.shape, quarterTensor_3x3_Stride2.shape ),
//         `DepthwiseConv2d_11x11_MultiStep() != DepthwiseConv2d_3x3_Stride2()`);
//
//       tf.util.assert(
//         tf.util.arraysEqual( quarterTensor_3x3_Stride2.shape, quarterTensor_ResizeBilinear.shape ),
//         `DepthwiseConv2d_3x3_Stride2() != ResizeBilinear()`);

      tf.util.assert(
        tf.util.arraysEqual( quarterTensor_ResizeBilinear.shape, quarterTensor_ResizeNearestNeighbor.shape ),
        `ResizeBilinear() != ResizeNearestNeighbor()`);

      // All test filters should generate same size result.
      this.testFiltersArray.forEach( ( testFilters, i ) => {
        let t = testFilters.apply( this.dataTensor3d, true );

        tf.util.assert(
          tf.util.arraysEqual( t.shape, quarterTensor_ResizeBilinear.shape ),
          `${testFilters.name}() != ResizeBilinear()`);

        t.dispose();

        yield* this.progressAdvanceYield();
      });

//    });

    // The above codes also compiles the codes.
    // Since the codes compiled, their execute time can be tested now.

    resultProfiles.push( await this.logProfile( "ResizeNearestNeighbor", this.test_ResizeNearestNeighbor.bind( this ) ) );
    yield* this.progressAdvanceYield();

    resultProfiles.push( await this.logProfile( "ResizeBilinear", this.test_ResizeBilinear.bind( this ) ) );
    yield* this.progressAdvanceYield();

    // All test filters in array.
    for ( let testFilters of this.testFiltersArray ) {
      // Note: Do not return result tensor so that need not to dispose them.
      resultProfiles.push(
        await this.logProfile( testFilters.name, testFilters.apply.bind( testFilters, this.dataTensor3d, false ) ) );

      yield* this.progressAdvanceYield();
    }

    return resultProfiles;
  }

  /**
   * Testing whether the results of different implementation are the same.
   *
   * @return {Object[]}
   *   Return array of profile infomation { title, backendName, newBytes, newTensors, peakBytes, kernelMs, wallMs }.
   */
  async generateProfiles() {
    let delayMilliseconds = 0;
    let generator = this.profilesGenerator();
    let resultProfiles = await PartTime.forOf(
      generator,
      ( valueMax ) => { this.progressReceiver.setValueMax( valueMax ); }, // Report progress to UI.
      delayMilliseconds
    );

    this.progressReceiver.informDone(); // Inform UI progress done.
    return resultProfiles;
  }

  /**
   * @return {Object}
   *   Return profile infomation { title, backendName, newBytes, newTensors, peakBytes, kernelMs, wallMs }.
   */
  async logProfile( title, func ) {

    // Get backend name before the following promise. Otherwise, it may be changed when the function been executed.
    let backendName = tf.getBackend();

    let profile = await tf.profile( func );
    let time = await tf.time( func );

    console.log(
       `${title} (${backendName}): `

     + `newBytes: ${profile.newBytes}, `
     + `newTensors: ${profile.newTensors}, `
     + `peakBytes: ${profile.peakBytes}, `
//     + `byte usage over all kernels: ${profile.kernels.map(k => k.totalBytesSnapshot)}, `

     + `kernelMs: ${time.kernelMs}, wallTimeMs: ${time.wallMs}`);

    let resultProfile = {
      title: title,
      backendName: backendName,
      newBytes: profile.newBytes,
      newTensors: profile.newTensors,
      peakBytes: profile.peakBytes,
      kernelMs: time.kernelMs,
      wallMs: time.wallMs
    };

    return resultProfile;
  }
}
<|endoftext|>


<commit_before>const qs = require('qs');
const hlpr = require('../lib/helpers');

const Activities = require('./activities');
const User = require('../models/user');

const logObj = {
  file: 'controllers/webhooks',
  logType: 'controller',
  level: 10,
};

/**
* will receive GET query string
*
* ?hub.mode=subscribe
* &hub.challenge=15f7d1a91c1f40f8a748fd134752feb3
* &hub.verify_token=STRAVA
*/
exports.stravaGetReceiver = (req, res) => {
  const q = qs.parse(req.query);
  const mssg = `stravaGetReceiver subscribing - local verify_token: ${process.env.STRAVA_VERIFY_TOKEN}, recieved verify_token: ${q['hub.verify_token']}`;
  if (process.env.STRAVA_VERIFY_TOKEN !== '' && q['hub.verify_token'] === process.env.STRAVA_VERIFY_TOKEN) {
    hlpr.logOutArgs(`${logObj.file}.stravaGetReceiver`, logObj.logType, 'success', 1, null, null, mssg);
    return res.send({ 'hub.challenge': q['hub.challenge'] });
  }
  hlpr.logOutArgs(`${logObj.file}.stravaGetReceiver 403`, logObj.logType, 'failure', 1, null, null, mssg);
  return res.status(403).send({ error: 'Faild to verify token' });
};

/**
* will receive POST body
*
req.body = {
  "aspect_type": "update", // Always "create," "update," or "delete."
  "event_time": 1516126040,
  "object_id": 1360128428,
  "object_type": "activity",
  "owner_id": 134815,
  "subscription_id": 120475,
  "updates": { // Empty for delete and create events.
    "title": "Messy",
    "type": "",
    "private": false, // Always true or false
  }
}
*/

const postProcessor = (input, done) => {
  const mssg = `stravaId: ${input.owner_id}, activityId: ${input.object_id}, aspect_type: ${input.aspect_type}`
  if (input.object_type === 'activity') {
    User.findOne({ stravaId: input.owner_id * 1 }, (err, user) => {
      if (err || !user) {
        hlpr.logOutArgs(`${logObj.file}.stravaPostReceiver`, logObj.logType, 'error', 4, null, null, `No User ${mssg}`);
        return done;
      }
      if (user.stravaId && user.clubMember) {
        const options = {
          id: user.stravaId,
          activityId: input.object_id,
          access_token: user.access_token,
          user,
        };
        const activity = { activityId: input.object_id };
        const toDelete = {
          activityId: input.object_id,
          'athlete.id': user.stravaId,
        };
        switch (input.aspect_type) {
          case 'create':
            return Activities.getActivityDetails(activity, options, cDone => cDone);
          case 'update':
            return Activities.getActivityUpdate(activity, options, cDone => cDone);
          case 'delete':
            return Activities.removeActivity(toDelete, rDone => rDone);
          default:
            hlpr.logOutArgs(`${logObj.file}.stravaPostReceiver`, logObj.logType, 'error', 4, null, null, `Webhook update- Missed Cases ${mssg}`);
        }
      } else {
        hlpr.logOutArgs(`${logObj.file}.stravaPostReceiver`, logObj.logType, 'success', 6, null, null, `Webhook update - Not a user or not clubMember ${mssg}`);
      }
      return done;
    });
    hlpr.logOutArgs(`${logObj.file}.stravaPostReceiver`, logObj.logType, 'success', 6, null, null, `Webhook update ${mssg}`);
  }
  return done;
};

exports.stravaPostReceiver = (req, res) => {
  postProcessor(req.body, logThis => logThis);
  res.sendStatus(200);
};
<commit_msg>last webhook logging update for tonight<commit_after>const qs = require('qs');
const hlpr = require('../lib/helpers');

const Activities = require('./activities');
const User = require('../models/user');

const logObj = {
  file: 'controllers/webhooks',
  logType: 'controller',
  level: 10,
};

/**
* will receive GET query string
*
* ?hub.mode=subscribe
* &hub.challenge=15f7d1a91c1f40f8a748fd134752feb3
* &hub.verify_token=STRAVA
*/
exports.stravaGetReceiver = (req, res) => {
  const q = qs.parse(req.query);
  const mssg = `stravaGetReceiver subscribing - local verify_token: ${process.env.STRAVA_VERIFY_TOKEN}, recieved verify_token: ${q['hub.verify_token']}`;
  if (process.env.STRAVA_VERIFY_TOKEN !== '' && q['hub.verify_token'] === process.env.STRAVA_VERIFY_TOKEN) {
    hlpr.logOutArgs(`${logObj.file}.stravaGetReceiver`, logObj.logType, 'success', 1, null, null, mssg);
    return res.send({ 'hub.challenge': q['hub.challenge'] });
  }
  hlpr.logOutArgs(`${logObj.file}.stravaGetReceiver 403`, logObj.logType, 'failure', 1, null, null, mssg);
  return res.status(403).send({ error: 'Faild to verify token' });
};

/**
* will receive POST body
*
req.body = {
  "aspect_type": "update", // Always "create," "update," or "delete."
  "event_time": 1516126040,
  "object_id": 1360128428,
  "object_type": "activity",
  "owner_id": 134815,
  "subscription_id": 120475,
  "updates": { // Empty for delete and create events.
    "title": "Messy",
    "type": "",
    "private": false, // Always true or false
  }
}
*/

const postProcessor = (input, done) => {
  const mssg = `stravaId: ${input.owner_id}, activityId: ${input.object_id}, aspect_type: ${input.aspect_type}`
  if (input.object_type === 'activity') {
    User.findOne({ stravaId: input.owner_id * 1 }, (err, user) => {
      if (err || !user) {
        hlpr.logOutArgs(`${logObj.file}.stravaPostReceiver`, logObj.logType, 'error', 4, null, null, `No User ${mssg}`);
        return done;
      }
      if (user.stravaId && user.clubMember) {
        const options = {
          id: user.stravaId,
          activityId: input.object_id,
          access_token: user.access_token,
          user,
        };
        const activity = { activityId: input.object_id };
        const toDelete = {
          activityId: input.object_id,
          'athlete.id': user.stravaId,
        };
        switch (input.aspect_type) {
          case 'create':
            return Activities.getActivityDetails(activity, options, cDone => cDone);
          case 'update':
            return Activities.getActivityUpdate(activity, options, cDone => cDone);
          case 'delete':
            return Activities.removeActivity(toDelete, rDone => rDone);
          default:
            hlpr.logOutArgs(`${logObj.file}.stravaPostReceiver`, logObj.logType, 'error', 4, null, null, `Webhook update- Missed Cases ${mssg}`);
        }
        hlpr.logOutArgs(`${logObj.file}.stravaPostReceiver`, logObj.logType, 'success', 6, null, null, `Webhook update ${mssg}`);
      } else {
        hlpr.logOutArgs(`${logObj.file}.stravaPostReceiver`, logObj.logType, 'success', 6, null, null, `Webhook update - Not a user or not clubMember ${mssg}`);
      }
    });
  }
  return done;
};

exports.stravaPostReceiver = (req, res) => {
  postProcessor(req.body, logThis => logThis);
  res.sendStatus(200);
};
<|endoftext|>


<commit_before>$(document).ready(function() {
    $("form").submit(function(e) {
        e.preventDefault();
        $("form button, form textarea").prop("disabled", true);
        $("form button").html(" Processing...");
        var students = $("#students").val().split("\n");
        var create_webdocs = function() {
            var students = students.splice(0, 10).join("\n");
            $.post("?json", { students: students, legacy: !!$("#legacy-checkbox").val(), "no-user": !!$("#no-user-checkbox").val() }, function(data) {
                $.each(data.success, function(k, v) {
                    var item = $("
");
                    item.text(v);
                    $("#success-list").append(item);
                });
                $.each(data.failure, function(k, v) {
                    var item = $("
");
                    item.text(v);
                    $("#failure-list").append(item);
                });
                if (students.length) {
                    create_webdocs();
                }
                else {
                    $("form").hide();
                    $("#finished-container").show();
                }
            }).fail(function() {
                Messenger().error("An error occured while creating webdocs.");
                $.each(students, function(k, v) {
                    var item = $("
");
                    item.text(v);
                    $("#failure-list").append(item);
                });
                $("form").hide();
                $("#finished-container").show();
            });
        };
        create_webdocs();
    });
});
<commit_msg>revert<commit_after>$(document).ready(function() {
    $("form").submit(function(e) {
        e.preventDefault();
        $("form button, form textarea").prop("disabled", true);
        $("form button").html(" Processing...");
        var students = $("#students").val().split("\n");
        var create_webdocs = function() {
            $.post("?json", { students: students.splice(0, 10).join("\n"), legacy: !!$("#legacy-checkbox").val(), "no-user": !!$("#no-user-checkbox").val() }, function(data) {
                $.each(data.success, function(k, v) {
                    var item = $("
");
                    item.text(v);
                    $("#success-list").append(item);
                });
                $.each(data.failure, function(k, v) {
                    var item = $("
");
                    item.text(v);
                    $("#failure-list").append(item);
                });
                if (students.length) {
                    create_webdocs();
                }
                else {
                    $("form").hide();
                    $("#finished-container").show();
                }
            }).fail(function() {
                Messenger().error("An error occured while creating webdocs.");
                $("form").hide();
                $("#finished-container").show();
            });
        };
        create_webdocs();
    });
});
<|endoftext|>


<commit_before>'use strict';

app
  .controller('LoadCtrl', ['$scope', '$cookies', '$location', '$alert', 'networksService', function ($scope, $cookies, $location, $alert, networksService) {
	networksService.getNetworksList().success(function(data) {
		$scope.networks = data;
	})
	.error(function(err) {
		$alert({title: 'Unable to load list of networks', content: err, placement: 'top', type: 'danger', show: true});
	});

	$scope.selectNetwork = function(id)
	{
		$cookies.put('activeNetworkID', id);
		$location.path('/core/workbench');
	}
  }]);<commit_msg>Bugfix. Now, network should be loaded via loadController to common service<commit_after>'use strict';

app
  .controller('LoadCtrl', ['$scope', '$cookies', '$location', '$alert', 'networksService', 'commonDataService', function ($scope, $cookies, $location, $alert, networksService, commonDataService) {
	networksService.getNetworksList().success(function(data) {
		$scope.networks = data;
	})
	.error(function(err) {
		$alert({title: 'Unable to load list of networks', content: err, placement: 'top', type: 'danger', show: true});
	});

	$scope.selectNetwork = function(id)
	{
		networksService.getParticularNetwork(activeNetworkID).success(function(data)
		{
			$scope.xmlNetwork = data;
		})
		.error(function(err)
		{
			$alert({title: 'Unable to load network', content: err, placement: 'top', type: 'danger', show: true});
		});

		$cookies.put('activeNetworkID', id);
		$location.path('/core/workbench');
	}
  }]);<|endoftext|>


<commit_before>func.drawBurnDown = function () {
    var $svg = $('#BurnDown');
    if ($svg.length !== 1) {
        return;
    }
    $svg.empty();
    var dataSet = JSON.parse($('#BurnDownJson').val());
    if (dataSet.length === 0) {
        $svg.hide();
        return;
    }
    $svg.show();
    var svg = d3.select('#BurnDown');
    var padding = 40;
    var axisPadding = 70;
    var width = parseInt(svg.style('width'));
    var height = parseInt(svg.style('height'));
    var bodyWidth = width - axisPadding - (padding);
    var bodyHeight = height - axisPadding - (padding);
    var minDate = new Date(d3.min(dataSet, function (d) { return d.Day; }));
    var maxDate = new Date(d3.max(dataSet, function (d) { return d.Day; }));
    var dayWidth = (bodyWidth - padding) / dateDiff('d', maxDate, minDate);
    var xScale = d3.time.scale()
        .domain([minDate, maxDate])
        .range([padding, bodyWidth]);
    var yScale = d3.scale.linear()
        .domain([d3.max(dataSet, function (d) {
            return Math.max.apply(null, [d.Total, d.Planned, d.Earned]);
        }), 0])
        .range([padding, bodyHeight])
        .nice();
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .tickFormat(d3.time.format('%m/%d'))
        .ticks(10);
    var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient('left');
    svg.append('g')
        .attr('class', 'axis')
        .attr('transform', 'translate(' + axisPadding + ', ' + (height - axisPadding) + ')')
        .call(xAxis)
        .selectAll('text')
        .attr('x', -20)
        .attr('y', 20)
        .style('text-anchor', 'start');
    svg.append('g')
        .attr('class', 'axis')
        .attr('transform', 'translate(' + axisPadding + ', 0)')
        .call(yAxis)
        .selectAll('text')
        .attr('x', -20);
    var now = axisPadding + xScale(new shortDate());
    var nowLineData = [
        [now, axisPadding - 40],
        [now, yScale(0) + 20]];
    var nowLine = d3.svg.line()
        .x(function (d) { return d[0]; })
        .y(function (d) { return d[1]; });
    svg.append('g').attr('class', 'now').append('path').attr('d', nowLine(nowLineData));
    draw('total', 0, dataSet.filter(function (d) { return d.Total !== undefined; }));
    draw('plan', 1, dataSet.filter(function (d) { return d.Planned !== undefined; }));
    draw('earn', 2, dataSet.filter(function (d) { return d.Earned !== undefined; }));

    function draw(css, n, ds) {
        var line = d3.svg.line()
            .x(function (d) {
                return (dateDiff('d', new Date(d.Day), minDate) * dayWidth)
                    + axisPadding + padding;
            })
            .y(function (d) {
                return yScale(prop(d));
            });
        var g = svg.append('g').attr('class', css);
        g.append('path').attr('d', line(ds));
        g.selectAll('circle')
            .data(ds)
            .enter()
            .append('circle')
            .attr('cx', function (d, i) { return i * dayWidth + axisPadding + padding })
            .attr('cy', function (d) { return yScale(prop(d)); })
            .attr('r', 4)
            .append('title')
            .text(function (d) { return prop(d); });

        function prop(d) {
            switch (n) {
                case 0: return d.Total;
                case 1: return d.Planned;
                case 2: return d.Earned;
            }
        }
    }

    $(document).on('click', '.burn-down-details-row', function () {
        if (!$(this).next().hasClass('burn-down-record-details')) {
            var formData = getFormData($(this));
            formData['BurnDownDate'] = $(this).attr('data-date');
            formData['BurnDownColspan'] = $(this).find('td').length;
            requestByForm(getForm($(this)), $(this));
        } else {
            $(this).next().remove();
        }
    });
    $(document).on('click', '.burn-down-record-details', function () {
        $(this).remove();
    });
}<commit_msg>Bug fixed: バーンダウンチャートでTotalまたはEarnedのデータが無い場合にスケールが正しく表示できない問題を解消。<commit_after>func.drawBurnDown = function () {
    var $svg = $('#BurnDown');
    if ($svg.length !== 1) {
        return;
    }
    $svg.empty();
    var dataSet = JSON.parse($('#BurnDownJson').val());
    if (dataSet.length === 0) {
        $svg.hide();
        return;
    }
    $svg.show();
    var svg = d3.select('#BurnDown');
    var padding = 40;
    var axisPadding = 70;
    var width = parseInt(svg.style('width'));
    var height = parseInt(svg.style('height'));
    var bodyWidth = width - axisPadding - (padding);
    var bodyHeight = height - axisPadding - (padding);
    var minDate = new Date(d3.min(dataSet, function (d) { return d.Day; }));
    var maxDate = new Date(d3.max(dataSet, function (d) { return d.Day; }));
    var dayWidth = (bodyWidth - padding) / dateDiff('d', maxDate, minDate);
    var xScale = d3.time.scale()
        .domain([minDate, maxDate])
        .range([padding, bodyWidth]);
    var yScale = d3.scale.linear()
        .domain([d3.max(dataSet, function (d) {
            return d.Total !== undefined || d.Earned !== undefined
                ? Math.max.apply(null, [d.Total, d.Planned, d.Earned])
                : d.Planned;
        }), 0])
        .range([padding, bodyHeight])
        .nice();
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .tickFormat(d3.time.format('%m/%d'))
        .ticks(10);
    var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient('left');
    svg.append('g')
        .attr('class', 'axis')
        .attr('transform', 'translate(' + axisPadding + ', ' + (height - axisPadding) + ')')
        .call(xAxis)
        .selectAll('text')
        .attr('x', -20)
        .attr('y', 20)
        .style('text-anchor', 'start');
    svg.append('g')
        .attr('class', 'axis')
        .attr('transform', 'translate(' + axisPadding + ', 0)')
        .call(yAxis)
        .selectAll('text')
        .attr('x', -20);
    var now = axisPadding + xScale(new shortDate());
    var nowLineData = [
        [now, axisPadding - 40],
        [now, yScale(0) + 20]];
    var nowLine = d3.svg.line()
        .x(function (d) { return d[0]; })
        .y(function (d) { return d[1]; });
    svg.append('g').attr('class', 'now').append('path').attr('d', nowLine(nowLineData));
    draw('total', 0, dataSet.filter(function (d) { return d.Total !== undefined; }));
    draw('plan', 1, dataSet.filter(function (d) { return d.Planned !== undefined; }));
    draw('earn', 2, dataSet.filter(function (d) { return d.Earned !== undefined; }));

    function draw(css, n, ds) {
        var line = d3.svg.line()
            .x(function (d) {
                return (dateDiff('d', new Date(d.Day), minDate) * dayWidth)
                    + axisPadding + padding;
            })
            .y(function (d) {
                return yScale(prop(d));
            });
        var g = svg.append('g').attr('class', css);
        g.append('path').attr('d', line(ds));
        g.selectAll('circle')
            .data(ds)
            .enter()
            .append('circle')
            .attr('cx', function (d, i) { return i * dayWidth + axisPadding + padding })
            .attr('cy', function (d) { return yScale(prop(d)); })
            .attr('r', 4)
            .append('title')
            .text(function (d) { return prop(d); });

        function prop(d) {
            switch (n) {
                case 0: return d.Total;
                case 1: return d.Planned;
                case 2: return d.Earned;
            }
        }
    }

    $(document).on('click', '.burn-down-details-row', function () {
        if (!$(this).next().hasClass('burn-down-record-details')) {
            var formData = getFormData($(this));
            formData['BurnDownDate'] = $(this).attr('data-date');
            formData['BurnDownColspan'] = $(this).find('td').length;
            requestByForm(getForm($(this)), $(this));
        } else {
            $(this).next().remove();
        }
    });
    $(document).on('click', '.burn-down-record-details', function () {
        $(this).remove();
    });
}<|endoftext|>