<commit_before>#include "libmesh/vectormap.h"
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>

#define VECTORMAPOBJECTTEST                     \
  CPPUNIT_TEST( testCreate );                   \
                                                \
using namespace libMesh;

class VectormapTest : public CppUnit::TestCase
{
public:
  CPPUNIT_TEST_SUITE ( VectormapTest );

  CPPUNIT_TEST( testCreate );
  CPPUNIT_TEST( testInsert );
  CPPUNIT_TEST( testIterate );

  CPPUNIT_TEST_SUITE_END();

private:

  template <typename Key, typename Val>
  void create()
  {
    vectormap<Key,Val> vm;
  }

  template <typename Key, typename Val>
  void insert()
  {
    vectormap<Key,Val> vm;

    Val val(0); // requires default constructor for val type.

    for (Key key=1; key<32; key*=2)
      vm.insert (std::make_pair(key,val));

    vm.sort();
  }

  template <typename Key, typename Val>
  void iterate(const Val &default_value=0)
  {
    vectormap<Key,Val> vm;

    Val val(default_value); // requires default constructor for val type.

    for (Key key=1; key<32; key*=2)
      vm.insert (std::make_pair(key,val));

    vm.sort();

    for (typename vectormap<Key,Val>::const_iterator it=vm.begin();
         it != vm.end(); ++it)
      {
        const Key &ikey = it->first;
        const Val &ival = it->second;

        CPPUNIT_ASSERT       ( vm.count(ikey) == 1 );
        CPPUNIT_ASSERT_EQUAL (vm[ikey], ival);
        CPPUNIT_ASSERT_EQUAL (ival, val);
      }
  }

public:

  // virtual void setUp()
  // {}

  // virtual void tearDown()
  // {}

  void testCreate()
  {
    create<int, int> ();
    create<int*,int> ();
    create<int*,int*>();
    create<int, std::vector<int> >();
  }

  void testInsert()
  {
    insert<int, int> ();
    insert<char,int> ();
    insert<long,int*>();
    insert<int, std::vector<int> >();
  }

  void testIterate()
  {
    iterate<int, int> ();
    iterate<char,int> ();
    iterate<long,int*>();
    iterate<int, std::string>("test_string");
  }
};

CPPUNIT_TEST_SUITE_REGISTRATION ( VectormapTest );
<commit_msg>Fixing macro bug introduced by whitespace scripts.<commit_after>#include "libmesh/vectormap.h"
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>

#define VECTORMAPOBJECTTEST                     \
  CPPUNIT_TEST( testCreate );                   \

using namespace libMesh;

class VectormapTest : public CppUnit::TestCase
{
public:
  CPPUNIT_TEST_SUITE ( VectormapTest );

  CPPUNIT_TEST( testCreate );
  CPPUNIT_TEST( testInsert );
  CPPUNIT_TEST( testIterate );

  CPPUNIT_TEST_SUITE_END();

private:

  template <typename Key, typename Val>
  void create()
  {
    vectormap<Key,Val> vm;
  }

  template <typename Key, typename Val>
  void insert()
  {
    vectormap<Key,Val> vm;

    Val val(0); // requires default constructor for val type.

    for (Key key=1; key<32; key*=2)
      vm.insert (std::make_pair(key,val));

    vm.sort();
  }

  template <typename Key, typename Val>
  void iterate(const Val &default_value=0)
  {
    vectormap<Key,Val> vm;

    Val val(default_value); // requires default constructor for val type.

    for (Key key=1; key<32; key*=2)
      vm.insert (std::make_pair(key,val));

    vm.sort();

    for (typename vectormap<Key,Val>::const_iterator it=vm.begin();
         it != vm.end(); ++it)
      {
        const Key &ikey = it->first;
        const Val &ival = it->second;

        CPPUNIT_ASSERT       ( vm.count(ikey) == 1 );
        CPPUNIT_ASSERT_EQUAL (vm[ikey], ival);
        CPPUNIT_ASSERT_EQUAL (ival, val);
      }
  }

public:

  // virtual void setUp()
  // {}

  // virtual void tearDown()
  // {}

  void testCreate()
  {
    create<int, int> ();
    create<int*,int> ();
    create<int*,int*>();
    create<int, std::vector<int> >();
  }

  void testInsert()
  {
    insert<int, int> ();
    insert<char,int> ();
    insert<long,int*>();
    insert<int, std::vector<int> >();
  }

  void testIterate()
  {
    iterate<int, int> ();
    iterate<char,int> ();
    iterate<long,int*>();
    iterate<int, std::string>("test_string");
  }
};

CPPUNIT_TEST_SUITE_REGISTRATION ( VectormapTest );
<|endoftext|>


<commit_before>#ifndef AMGCL_RELAXATION_CHEBYSHEV_HPP
#define AMGCL_RELAXATION_CHEBYSHEV_HPP

/*
The MIT License

Copyright (c) 2012-2019 Denis Demidov <dennis.demidov@gmail.com>
Copyright (c) 2019 Peter Gamnitzer, UIBK (University of Innsbruck)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   amgcl/relaxation/chebyshev.hpp
 * \author Denis Demidov <dennis.demidov@gmail.com>
 * \brief  Chebyshev polynomial smoother.
 *
 * Implements Algorithm 1 from
 * P. Ghysels, P. Kłosiewicz, and W. Vanroose.
 * "Improving the arithmetic intensity of multigrid with the help of polynomial smoothers".
 * Numer. Linear Algebra Appl. 2012;19:253-267. DOI: 10.1002/nla.1808
 */

#include <vector>
#include <cmath>

#include <amgcl/detail/inverse.hpp>
#include <amgcl/util.hpp>

namespace amgcl {
namespace relaxation {

/// Chebyshev polynomial smoother.
/**
 * \param Backend Backend for temporary structures allocation.
 * \ingroup relaxation
 */
template <class Backend>
class chebyshev {
    public:
        typedef typename Backend::value_type value_type;
        typedef typename Backend::vector     vector;

        typedef typename math::scalar_of<value_type>::type scalar_type;

        /// Relaxation parameters.
        struct params {
            /// Chebyshev polynomial degree.
            unsigned degree;

            /// highest eigen value safety upscaling.
            // use boosting factor for a more conservative upper bound estimate
            // See: Adams, Brezina, Hu, Tuminaro,
            //      PARALLEL MULTIGRID SMOOTHING: POLYNOMIAL VERSUS
            //      GAUSS-SEIDEL, J. Comp. Phys. 188 (2003) 593-610.
            //
            float higher;

            /// Lowest-to-highest eigen value ratio.
            float lower;

            // Number of power iterations to apply for the spectral radius
            // estimation. When 0, use Gershgorin disk theorem to estimate
            // spectral radius.
            int power_iters;

            // Scale the system matrix
            bool scale;

            params()
                : degree(5), higher(1.0f), lower(1.0f / 30), power_iters(0),
                  scale(false)
            {}

#ifndef AMGCL_NO_BOOST
            params(const boost::property_tree::ptree &p)
                : AMGCL_PARAMS_IMPORT_VALUE(p, degree),
                  AMGCL_PARAMS_IMPORT_VALUE(p, higher),
                  AMGCL_PARAMS_IMPORT_VALUE(p, lower),
                  AMGCL_PARAMS_IMPORT_VALUE(p, power_iters),
                  AMGCL_PARAMS_IMPORT_VALUE(p, scale)
            {
                check_params(p, {"degree", "higher", "lower", "power_iters", "scale"});
            }

            void get(boost::property_tree::ptree &p, const std::string &path) const {
                AMGCL_PARAMS_EXPORT_VALUE(p, path, degree);
                AMGCL_PARAMS_EXPORT_VALUE(p, path, higher);
                AMGCL_PARAMS_EXPORT_VALUE(p, path, lower);
                AMGCL_PARAMS_EXPORT_VALUE(p, path, power_iters);
                AMGCL_PARAMS_EXPORT_VALUE(p, path, scale);
            }
#endif
        } prm;

        /// \copydoc amgcl::relaxation::damped_jacobi::damped_jacobi
        template <class Matrix>
        chebyshev(
                const Matrix &A, const params &prm,
                const typename Backend::params &backend_prm
            ) : prm(prm),
                M( Backend::copy_vector( diagonal(A, /*invert*/true), backend_prm ) ),
                p( Backend::create_vector(rows(A), backend_prm) ),
                r( Backend::create_vector(rows(A), backend_prm) )
        {
            scalar_type hi, lo;

            if (prm.scale) {
                hi = backend::spectral_radius<true>(A, prm.power_iters);
            } else {
                hi = backend::spectral_radius<false>(A, prm.power_iters);
            }

            lo = hi * prm.lower;
            hi *= prm.higher;

            // Centre of ellipse containing the eigenvalues of A:
            d = 0.5 * (hi + lo);

            // Semi-major axis of ellipse containing the eigenvalues of A:
            c = 0.5 * (hi - lo);
        }

        template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
        void apply_pre(
                const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP&
                ) const
        {
            solve(A, rhs, x);
        }

        template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
        void apply_post(
                const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP&
                ) const
        {
            solve(A, rhs, x);
        }

        template <class Matrix, class VectorRHS, class VectorX>
        void apply(const Matrix &A, const VectorRHS &rhs, VectorX &x) const
        {
            backend::clear(x);
            solve(A, rhs, x);
        }

        size_t bytes() const {
            return backend::bytes(*M) + backend::bytes(*p) + backend::bytes(*r);
        }

    private:
        std::shared_ptr<typename Backend::matrix_diagonal> M;
        mutable std::shared_ptr<vector> p, r;

        scalar_type c, d;

        template <class Matrix, class VectorB, class VectorX>
        void solve(const Matrix &A, const VectorB &b, VectorX &x) const
        {
            static const scalar_type one  = math::identity<scalar_type>();
            static const scalar_type zero = math::zero<scalar_type>();

            scalar_type alpha, beta;

            for (unsigned k = 0; k < prm.degree; ++k) {
                backend::residual(b, A, x, *r);

                if (prm.scale) backend::vmul(one, *M, *r, zero, *r);

                if (k == 0) {
                    alpha = math::inverse(d);
                    beta  = zero;
                } else if (k == 1) {
                    alpha = 2 * d * math::inverse(2 * d * d - c * c);
                    beta  = alpha * d - one;
                } else {
                    alpha = math::inverse(d - 0.25 * alpha * c * c);
                    beta  = alpha * d - one;
                }

                backend::axpby(alpha, *r, beta, *p);
                backend::axpby(one, *p, one, x);
            }
        }
};

} // namespace relaxation
} // namespace amgcl

#endif
<commit_msg>Chebyshev: do not allocate/compute diagonal when not scaling<commit_after>#ifndef AMGCL_RELAXATION_CHEBYSHEV_HPP
#define AMGCL_RELAXATION_CHEBYSHEV_HPP

/*
The MIT License

Copyright (c) 2012-2019 Denis Demidov <dennis.demidov@gmail.com>
Copyright (c) 2019 Peter Gamnitzer, UIBK (University of Innsbruck)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   amgcl/relaxation/chebyshev.hpp
 * \author Denis Demidov <dennis.demidov@gmail.com>
 * \brief  Chebyshev polynomial smoother.
 *
 * Implements Algorithm 1 from
 * P. Ghysels, P. Kłosiewicz, and W. Vanroose.
 * "Improving the arithmetic intensity of multigrid with the help of polynomial smoothers".
 * Numer. Linear Algebra Appl. 2012;19:253-267. DOI: 10.1002/nla.1808
 */

#include <vector>
#include <cmath>

#include <amgcl/detail/inverse.hpp>
#include <amgcl/util.hpp>

namespace amgcl {
namespace relaxation {

/// Chebyshev polynomial smoother.
/**
 * \param Backend Backend for temporary structures allocation.
 * \ingroup relaxation
 */
template <class Backend>
class chebyshev {
    public:
        typedef typename Backend::value_type value_type;
        typedef typename Backend::vector     vector;

        typedef typename math::scalar_of<value_type>::type scalar_type;

        /// Relaxation parameters.
        struct params {
            /// Chebyshev polynomial degree.
            unsigned degree;

            /// highest eigen value safety upscaling.
            // use boosting factor for a more conservative upper bound estimate
            // See: Adams, Brezina, Hu, Tuminaro,
            //      PARALLEL MULTIGRID SMOOTHING: POLYNOMIAL VERSUS
            //      GAUSS-SEIDEL, J. Comp. Phys. 188 (2003) 593-610.
            //
            float higher;

            /// Lowest-to-highest eigen value ratio.
            float lower;

            // Number of power iterations to apply for the spectral radius
            // estimation. When 0, use Gershgorin disk theorem to estimate
            // spectral radius.
            int power_iters;

            // Scale the system matrix
            bool scale;

            params()
                : degree(5), higher(1.0f), lower(1.0f / 30), power_iters(0),
                  scale(false)
            {}

#ifndef AMGCL_NO_BOOST
            params(const boost::property_tree::ptree &p)
                : AMGCL_PARAMS_IMPORT_VALUE(p, degree),
                  AMGCL_PARAMS_IMPORT_VALUE(p, higher),
                  AMGCL_PARAMS_IMPORT_VALUE(p, lower),
                  AMGCL_PARAMS_IMPORT_VALUE(p, power_iters),
                  AMGCL_PARAMS_IMPORT_VALUE(p, scale)
            {
                check_params(p, {"degree", "higher", "lower", "power_iters", "scale"});
            }

            void get(boost::property_tree::ptree &p, const std::string &path) const {
                AMGCL_PARAMS_EXPORT_VALUE(p, path, degree);
                AMGCL_PARAMS_EXPORT_VALUE(p, path, higher);
                AMGCL_PARAMS_EXPORT_VALUE(p, path, lower);
                AMGCL_PARAMS_EXPORT_VALUE(p, path, power_iters);
                AMGCL_PARAMS_EXPORT_VALUE(p, path, scale);
            }
#endif
        } prm;

        /// \copydoc amgcl::relaxation::damped_jacobi::damped_jacobi
        template <class Matrix>
        chebyshev(
                const Matrix &A, const params &prm,
                const typename Backend::params &backend_prm
            ) : prm(prm),
                p( Backend::create_vector(rows(A), backend_prm) ),
                r( Backend::create_vector(rows(A), backend_prm) )
        {
            scalar_type hi, lo;

            if (prm.scale) {
                M  = Backend::copy_vector( diagonal(A, /*invert*/true), backend_prm );
                hi = backend::spectral_radius<true>(A, prm.power_iters);
            } else {
                hi = backend::spectral_radius<false>(A, prm.power_iters);
            }

            lo = hi * prm.lower;
            hi *= prm.higher;

            // Centre of ellipse containing the eigenvalues of A:
            d = 0.5 * (hi + lo);

            // Semi-major axis of ellipse containing the eigenvalues of A:
            c = 0.5 * (hi - lo);
        }

        template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
        void apply_pre(
                const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP&
                ) const
        {
            solve(A, rhs, x);
        }

        template <class Matrix, class VectorRHS, class VectorX, class VectorTMP>
        void apply_post(
                const Matrix &A, const VectorRHS &rhs, VectorX &x, VectorTMP&
                ) const
        {
            solve(A, rhs, x);
        }

        template <class Matrix, class VectorRHS, class VectorX>
        void apply(const Matrix &A, const VectorRHS &rhs, VectorX &x) const
        {
            backend::clear(x);
            solve(A, rhs, x);
        }

        size_t bytes() const {
            size_t b = backend::bytes(*p) + backend::bytes(*r);
            if (prm.scale) b += backend::bytes(*M);
            return b;
        }

    private:
        std::shared_ptr<typename Backend::matrix_diagonal> M;
        mutable std::shared_ptr<vector> p, r;

        scalar_type c, d;

        template <class Matrix, class VectorB, class VectorX>
        void solve(const Matrix &A, const VectorB &b, VectorX &x) const
        {
            static const scalar_type one  = math::identity<scalar_type>();
            static const scalar_type zero = math::zero<scalar_type>();

            scalar_type alpha, beta;

            for (unsigned k = 0; k < prm.degree; ++k) {
                backend::residual(b, A, x, *r);

                if (prm.scale) backend::vmul(one, *M, *r, zero, *r);

                if (k == 0) {
                    alpha = math::inverse(d);
                    beta  = zero;
                } else if (k == 1) {
                    alpha = 2 * d * math::inverse(2 * d * d - c * c);
                    beta  = alpha * d - one;
                } else {
                    alpha = math::inverse(d - 0.25 * alpha * c * c);
                    beta  = alpha * d - one;
                }

                backend::axpby(alpha, *r, beta, *p);
                backend::axpby(one, *p, one, x);
            }
        }
};

} // namespace relaxation
} // namespace amgcl

#endif
<|endoftext|>


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

#include "scanner/evaluators/caffe/default/default_input_evaluator.h"
#include "scanner/util/memory.h"

namespace scanner {

DefaultInputEvaluator::DefaultInputEvaluator(DeviceType device_type,
                                             i32 device_id,
                                             const NetDescriptor& descriptor,
                                             i32 batch_size)
    : device_type_(device_type),
      device_id_(device_id),
      descriptor_(descriptor),
      batch_size_(batch_size) {
  if (descriptor_.input_width != -1) {
    net_input_width_ = descriptor_.input_width;
    net_input_height_ = descriptor_.input_height;
  } else {
    net_input_width_ = -1;
    net_input_height_ = -1;
  }

  std::vector<float>& mean_colors = descriptor_.mean_colors;
  caffe::TransformationParameter param;
  param.set_force_color(true);
  param.set_scale(1.0 / 255.0);
  for (i32 i = 0; i < mean_colors.size(); i++) {
    param.add_mean_value(mean_colors[i]);
  }
  transformer =
      std::make_unique<caffe::DataTransformer<f32>>(param, caffe::TEST);

  resized_input = cv::Mat(net_input_height_, net_input_width_, CV_8UC3);
  for (i32 i = 0; i < batch_size_; ++i) {
    input_mats.emplace_back(
        cv::Mat(net_input_height_, net_input_width_, CV_8UC3));
  }
}

void DefaultInputEvaluator::configure(const VideoMetadata& metadata) {
  metadata_ = metadata;

  i32 width = metadata.width();
  i32 height = metadata.height();
  if (net_input_width_ == -1) {
    net_input_width_ = width;
    net_input_height_ = height;
  }

  output_blob.Reshape(batch_size_, 3, net_input_height_, net_input_width_);
}

void DefaultInputEvaluator::evaluate(
    const std::vector<std::vector<u8*>>& input_buffers,
    const std::vector<std::vector<size_t>>& input_sizes,
    std::vector<std::vector<u8*>>& output_buffers,
    std::vector<std::vector<size_t>>& output_sizes) {
  auto eval_start = now();

  size_t frame_size = net_input_width_ * net_input_height_ * 3 * sizeof(float);
  i32 input_count = input_buffers[0].size();

  if (device_type_ == DeviceType::GPU) {
    LOG(FATAL) << "Default input transformer doesn't support GPU mode.";
  } else {
    for (i32 frame = 0; frame < input_count; frame += batch_size_) {
      i32 batch_count = std::min(input_count - frame, batch_size_);

      i32 frame_width = metadata_.width();
      i32 frame_height = metadata_.height();

      for (i32 i = 0; i < batch_count; ++i) {
        u8* buffer = input_buffers[0][frame + i];
        cv::Mat input_mat(frame_height, frame_width, CV_8UC3, buffer);

        cv::resize(input_mat, resized_input,
                   cv::Size(net_input_width_, net_input_height_), 0, 0,
                   cv::INTER_LINEAR);
        cv::cvtColor(resized_input, input_mats[i], CV_RGB2BGR);
      }

      std::vector<cv::Mat> input_mats_slice(input_mats.begin(),
                                            input_mats.begin() + batch_count);

      output_blob.Reshape(input_mats_slice.size(), output_blob.shape(1),
                          output_blob.shape(2), output_blob.shape(3));
      transformer->Transform(input_mats_slice, &output_blob);

      u8* net_input = new u8[frame_size * batch_count];
      std::memcpy(net_input, output_blob.cpu_data(), frame_size * batch_count);

      output_buffers[0].push_back(net_input);
      output_sizes[0].push_back(frame_size * batch_count);
    }

    i32 num_batches = output_buffers[0].size();
    for (i32 i = 0; i < input_buffers[0].size() - num_batches; ++i) {
      output_buffers[0].push_back(new u8[1]);
      output_sizes[0].push_back(1);
    }
  }

  for (i32 i = 0; i < input_buffers[0].size(); ++i) {
    size_t size = input_sizes[0][i];
    u8* buffer = new_buffer(device_type_, device_id_, size);
    memcpy_buffer(buffer, device_type_, device_id_, input_buffers[0][i],
                  device_type_, device_id_, size);
    output_buffers[1].push_back(buffer);
    output_sizes[1].push_back(size);
  }

  if (profiler_) {
    profiler_->add_interval("caffe:transform_input", eval_start, now());
  }
}

DefaultInputEvaluatorFactory::DefaultInputEvaluatorFactory(
    DeviceType device_type, const NetDescriptor& descriptor, i32 batch_size)
    : device_type_(device_type),
      net_descriptor_(descriptor),
      batch_size_(batch_size) {}

EvaluatorCapabilities DefaultInputEvaluatorFactory::get_capabilities() {
  EvaluatorCapabilities caps;
  caps.device_type = device_type_;
  if (device_type_ == DeviceType::GPU) {
    caps.max_devices = 1;
  } else {
    caps.max_devices = EvaluatorCapabilities::UnlimitedDevices;
  }
  caps.warmup_size = 0;
  return caps;
}

std::vector<std::string> DefaultInputEvaluatorFactory::get_output_names() {
  return {"net_input", "frame"};
}

Evaluator* DefaultInputEvaluatorFactory::new_evaluator(
    const EvaluatorConfig& config) {
  return new DefaultInputEvaluator(device_type_, config.device_ids[0],
                                   net_descriptor_, batch_size_);
}
}
<commit_msg>Reduced memory allocation in default evaluator<commit_after>/* Copyright 2016 Carnegie Mellon University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "scanner/evaluators/caffe/default/default_input_evaluator.h"
#include "scanner/util/memory.h"

namespace scanner {

DefaultInputEvaluator::DefaultInputEvaluator(DeviceType device_type,
                                             i32 device_id,
                                             const NetDescriptor& descriptor,
                                             i32 batch_size)
    : device_type_(device_type),
      device_id_(device_id),
      descriptor_(descriptor),
      batch_size_(batch_size) {
  if (descriptor_.input_width != -1) {
    net_input_width_ = descriptor_.input_width;
    net_input_height_ = descriptor_.input_height;
  } else {
    net_input_width_ = -1;
    net_input_height_ = -1;
  }

  std::vector<float>& mean_colors = descriptor_.mean_colors;
  caffe::TransformationParameter param;
  param.set_force_color(true);
  param.set_scale(1.0 / 255.0);
  for (i32 i = 0; i < mean_colors.size(); i++) {
    param.add_mean_value(mean_colors[i]);
  }
  transformer =
      std::make_unique<caffe::DataTransformer<f32>>(param, caffe::TEST);

  resized_input = cv::Mat(net_input_height_, net_input_width_, CV_8UC3);
  for (i32 i = 0; i < batch_size_; ++i) {
    input_mats.emplace_back(
        cv::Mat(net_input_height_, net_input_width_, CV_8UC3));
  }
}

void DefaultInputEvaluator::configure(const VideoMetadata& metadata) {
  metadata_ = metadata;

  i32 width = metadata.width();
  i32 height = metadata.height();
  if (net_input_width_ == -1) {
    net_input_width_ = width;
    net_input_height_ = height;
  }

  output_blob.Reshape(batch_size_, 3, net_input_height_, net_input_width_);
}

void DefaultInputEvaluator::evaluate(
    const std::vector<std::vector<u8*>>& input_buffers,
    const std::vector<std::vector<size_t>>& input_sizes,
    std::vector<std::vector<u8*>>& output_buffers,
    std::vector<std::vector<size_t>>& output_sizes) {
  auto eval_start = now();

  size_t frame_size = net_input_width_ * net_input_height_ * 3 * sizeof(float);
  i32 input_count = input_buffers[0].size();

  if (device_type_ == DeviceType::GPU) {
    LOG(FATAL) << "Default input transformer doesn't support GPU mode.";
  } else {
    for (i32 frame = 0; frame < input_count; frame += batch_size_) {
      i32 batch_count = std::min(input_count - frame, batch_size_);

      i32 frame_width = metadata_.width();
      i32 frame_height = metadata_.height();

      for (i32 i = 0; i < batch_count; ++i) {
        u8* buffer = input_buffers[0][frame + i];
        cv::Mat input_mat(frame_height, frame_width, CV_8UC3, buffer);

        cv::resize(input_mat, resized_input,
                   cv::Size(net_input_width_, net_input_height_), 0, 0,
                   cv::INTER_LINEAR);
        cv::cvtColor(resized_input, input_mats[i], CV_RGB2BGR);
      }

      std::vector<cv::Mat> input_mats_slice(input_mats.begin(),
                                            input_mats.begin() + batch_count);

      u8* net_input = new u8[frame_size * batch_count];
      output_blob.set_cpu_data((f32*)net_input);
      output_blob.Reshape(input_mats_slice.size(), output_blob.shape(1),
                          output_blob.shape(2), output_blob.shape(3));
      transformer->Transform(input_mats_slice, &output_blob);

      output_buffers[0].push_back(net_input);
      output_sizes[0].push_back(frame_size * batch_count);
    }

    i32 num_batches = output_buffers[0].size();
    for (i32 i = 0; i < input_buffers[0].size() - num_batches; ++i) {
      output_buffers[0].push_back(new u8[1]);
      output_sizes[0].push_back(1);
    }
  }

  for (i32 i = 0; i < input_buffers[0].size(); ++i) {
    size_t size = input_sizes[0][i];
    u8* buffer = new_buffer(device_type_, device_id_, size);
    memcpy_buffer(buffer, device_type_, device_id_, input_buffers[0][i],
                  device_type_, device_id_, size);
    output_buffers[1].push_back(buffer);
    output_sizes[1].push_back(size);
  }

  if (profiler_) {
    profiler_->add_interval("caffe:transform_input", eval_start, now());
  }
}

DefaultInputEvaluatorFactory::DefaultInputEvaluatorFactory(
    DeviceType device_type, const NetDescriptor& descriptor, i32 batch_size)
    : device_type_(device_type),
      net_descriptor_(descriptor),
      batch_size_(batch_size) {}

EvaluatorCapabilities DefaultInputEvaluatorFactory::get_capabilities() {
  EvaluatorCapabilities caps;
  caps.device_type = device_type_;
  if (device_type_ == DeviceType::GPU) {
    caps.max_devices = 1;
  } else {
    caps.max_devices = EvaluatorCapabilities::UnlimitedDevices;
  }
  caps.warmup_size = 0;
  return caps;
}

std::vector<std::string> DefaultInputEvaluatorFactory::get_output_names() {
  return {"net_input", "frame"};
}

Evaluator* DefaultInputEvaluatorFactory::new_evaluator(
    const EvaluatorConfig& config) {
  return new DefaultInputEvaluator(device_type_, config.device_ids[0],
                                   net_descriptor_, batch_size_);
}
}
<|endoftext|>


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

#include "scanner/evaluators/caffe/facenet/facenet_input_evaluator.h"
#include "scanner/util/memory.h"

namespace scanner {

FacenetInputEvaluator::FacenetInputEvaluator(DeviceType device_type,
                                             i32 device_id,
                                             const NetDescriptor& descriptor,
                                             i32 batch_size,
                                             f32 scale)
    : device_type_(device_type),
      device_id_(device_id),
      descriptor_(descriptor),
      scale_(scale),
      batch_size_(batch_size),
      net_input_width_(224),
      net_input_height_(224)
#ifdef HAVE_CUDA
      ,
      num_cuda_streams_(32),
      streams_(num_cuda_streams_)
#endif
{
}

void FacenetInputEvaluator::configure(const BatchConfig& config) {
  config_ = config;
  assert(config.formats.size() == 1);
  metadata_ = config.formats[0];

  net_input_width_ = metadata_.width() * scale_;
  net_input_height_ = metadata_.height() * scale_;

  net_input_width_ += (net_input_width_ % 8);
  net_input_height_ += (net_input_height_ % 8);

  if (device_type_ == DeviceType::GPU) {
#ifdef HAVE_CUDA
    cv::cuda::setDevice(device_id_);
    cudaSetDevice(device_id_);

    mean_mat_g_ = cv::cuda::GpuMat(
        net_input_height_, net_input_width_, CV_32FC3,
        cv::Scalar(descriptor_.mean_colors[0], descriptor_.mean_colors[1],
                   descriptor_.mean_colors[2]));

    frame_input_g_.clear();
    float_input_g_.clear();
    normalized_input_g_.clear();
    input_planes_g_.clear();
    planar_input_g_.clear();
    for (size_t i = 0; i < num_cuda_streams_; ++i) {
      frame_input_g_.push_back(
          cv::cuda::GpuMat(metadata_.height(), metadata_.width(), CV_8UC3));
      resized_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_8UC3));
      float_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      normalized_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      std::vector<cv::cuda::GpuMat> planes;
      std::vector<cv::cuda::GpuMat> flipped_planes;
      for (i32 i = 0; i < 3; ++i) {
        planes.push_back(
            cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC1));
        flipped_planes.push_back(
            cv::cuda::GpuMat(net_input_width_, net_input_height_, CV_32FC1));
      }
      input_planes_g_.push_back(planes);
      flipped_planes_g_.push_back(flipped_planes);
      planar_input_g_.push_back(
          cv::cuda::GpuMat(net_input_width_ * 3, net_input_height_, CV_32FC1));
    }
#else
    LOG(FATAL) << "Not built with CUDA support.";
#endif
  } else {
    mean_mat_c_ = cv::Mat(
        net_input_height_, net_input_width_, CV_32FC3,
        cv::Scalar(descriptor_.mean_colors[0], descriptor_.mean_colors[1],
                   descriptor_.mean_colors[2]));

    float_input_c_ = cv::Mat(net_input_height_, net_input_width_, CV_32FC3);
    normalized_input_c_ =
        cv::Mat(net_input_height_, net_input_width_, CV_32FC3);
    for (i32 i = 0; i < 3; ++i) {
      input_planes_c_.push_back(
          cv::Mat(net_input_height_, net_input_width_, CV_32FC1));
    }
    planar_input_c_ =
        cv::Mat(net_input_height_ * 3, net_input_width_, CV_32FC1);
  }
}

void FacenetInputEvaluator::evaluate(const BatchedColumns& input_columns,
                                     BatchedColumns& output_columns) {
  auto eval_start = now();

  size_t frame_size = net_input_width_ * net_input_height_ * 3;
  i32 input_count = input_columns[0].rows.size();
  i32 net_input_size = frame_size * sizeof(f32);

  u8* output_block = new_block_buffer({device_type_, device_id_},
                                      input_count * net_input_size,
                                      input_count);

  if (device_type_ == DeviceType::GPU) {
#ifdef HAVE_CUDA
    streams_.resize(0);
    streams_.resize(num_cuda_streams_);

    for (i32 i = 0; i < input_count; ++i) {
      int sid = i % num_cuda_streams_;
      cv::cuda::Stream& cv_stream = streams_[sid];

      f32* net_input = (f32*) (output_block + i * net_input_size);

      u8 *buffer = input_columns[0].rows[i].buffer;
      frame_input_g_[sid] = cv::cuda::GpuMat(
          metadata_.height(), metadata_.width(), CV_8UC3, buffer);
      cv::cuda::resize(frame_input_g_[sid], resized_input_g_[sid],
                       cv::Size(net_input_width_, net_input_height_), 0, 0,
                       cv::INTER_LINEAR, cv_stream);
      resized_input_g_[sid].convertTo(float_input_g_[sid], CV_32FC3, cv_stream);
      cv::cuda::subtract(float_input_g_[sid], mean_mat_g_,
                         normalized_input_g_[sid], cv::noArray(), -1,
                         cv_stream);
      // Changed from interleaved RGB to planar RGB
      cv::cuda::split(normalized_input_g_[sid], input_planes_g_[sid],
                      cv_stream);
      cv::cuda::transpose(input_planes_g_[sid][0], flipped_planes_g_[sid][0],
                          cv_stream);
      cv::cuda::transpose(input_planes_g_[sid][1], flipped_planes_g_[sid][1],
                          cv_stream);
      cv::cuda::transpose(input_planes_g_[sid][2], flipped_planes_g_[sid][2],
                          cv_stream);
      auto& plane1 = flipped_planes_g_[sid][0];
      auto& plane2 = flipped_planes_g_[sid][1];
      auto& plane3 = flipped_planes_g_[sid][2];
      auto& planar_input = planar_input_g_[sid];
      plane1.copyTo(planar_input(cv::Rect(
          0, net_input_width_ * 0, net_input_height_, net_input_width_)));
      plane2.copyTo(planar_input(cv::Rect(
          0, net_input_width_ * 1, net_input_height_, net_input_width_)));
      plane3.copyTo(planar_input(cv::Rect(
          0, net_input_width_ * 2, net_input_height_, net_input_width_)));
      assert(planar_input.cols == net_input_height_);
      cudaStream_t s = cv::cuda::StreamAccessor::getStream(cv_stream);
      CU_CHECK(cudaMemcpy2DAsync(
          net_input, net_input_height_ * sizeof(float),
          planar_input.data, planar_input.step,
          net_input_height_ * sizeof(float),
          net_input_width_ * 3, cudaMemcpyDeviceToDevice, s));

      INSERT_ROW(output_columns[1], (u8*)net_input, net_input_size);
    }
    for (cv::cuda::Stream& s : streams_) {
      s.waitForCompletion();
    }
#else
    LOG(FATAL) << "Not built with CUDA support.";
#endif
  } else {
    for (i32 i = 0; i < input_count; ++i) {
      u8* buffer = input_columns[0].rows[i].buffer;
      f32* net_input = reinterpret_cast<f32*>(new u8[frame_size * sizeof(f32)]);

      cv::Mat input_mat =
          cv::Mat(net_input_height_, net_input_width_, CV_8UC3, buffer);

      // Changed from interleaved RGB to planar RGB
      input_mat.convertTo(float_input_c_, CV_32FC3);
      cv::subtract(float_input_c_, mean_mat_c_, normalized_input_c_);
      cv::split(normalized_input_c_, input_planes_c_);
      cv::vconcat(input_planes_c_, planar_input_c_);
      assert(planar_input_c_.cols == net_input_height_);
      for (i32 r = 0; r < planar_input_c_.rows; ++r) {
        u8* mat_pos = planar_input_c_.data + r * planar_input_c_.step;
        u8* input_pos = reinterpret_cast<u8*>(
            net_input + i * (net_input_width_ * net_input_height_ * 3) +
            r * net_input_width_);
        std::memcpy(input_pos, mat_pos, planar_input_c_.cols * sizeof(float));
      }

      INSERT_ROW(output_columns[1], (u8*)net_input, frame_size * sizeof(f32));
    }
  }

  for (i32 i = 0; i < input_columns[0].rows.size(); ++i) {
    INSERT_ROW(output_columns[0], input_columns[0].rows[i].buffer,
               input_columns[0].rows[i].size);
  }

  if (profiler_) {
    profiler_->add_interval("facenet_input", eval_start, now());
  }
}

FacenetInputEvaluatorFactory::FacenetInputEvaluatorFactory(
    DeviceType device_type, const NetDescriptor& descriptor, i32 batch_size,
    f32 scale)
    : device_type_(device_type),
      net_descriptor_(descriptor),
      batch_size_(batch_size),
      scale_(scale) {}

EvaluatorCapabilities FacenetInputEvaluatorFactory::get_capabilities() {
  EvaluatorCapabilities caps;
  caps.device_type = device_type_;
  if (device_type_ == DeviceType::GPU) {
    caps.max_devices = 1;
  } else {
    caps.max_devices = EvaluatorCapabilities::UnlimitedDevices;
  }
  caps.warmup_size = 0;
  return caps;
}

std::vector<std::string> FacenetInputEvaluatorFactory::get_output_columns(
    const std::vector<std::string>& input_columns) {
  return {"frame", "net_input"};
}

Evaluator *
FacenetInputEvaluatorFactory::new_evaluator(const EvaluatorConfig &config) {
  return new FacenetInputEvaluator(device_type_, config.device_ids[0],
                                   net_descriptor_, batch_size_, scale_);
}
}
<commit_msg>Fix bug in facenet input evaluator<commit_after>/* Copyright 2016 Carnegie Mellon University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "scanner/evaluators/caffe/facenet/facenet_input_evaluator.h"
#include "scanner/util/memory.h"

namespace scanner {

FacenetInputEvaluator::FacenetInputEvaluator(DeviceType device_type,
                                             i32 device_id,
                                             const NetDescriptor& descriptor,
                                             i32 batch_size,
                                             f32 scale)
    : device_type_(device_type),
      device_id_(device_id),
      descriptor_(descriptor),
      scale_(scale),
      batch_size_(batch_size),
      net_input_width_(224),
      net_input_height_(224)
#ifdef HAVE_CUDA
      ,
      num_cuda_streams_(32),
      streams_(num_cuda_streams_)
#endif
{
}

void FacenetInputEvaluator::configure(const BatchConfig& config) {
  config_ = config;
  assert(config.formats.size() == 1);
  metadata_ = config.formats[0];

  net_input_width_ = metadata_.width() * scale_;
  net_input_height_ = metadata_.height() * scale_;

  net_input_width_ += (net_input_width_ % 8);
  net_input_height_ += (net_input_height_ % 8);

  if (device_type_ == DeviceType::GPU) {
#ifdef HAVE_CUDA
    cv::cuda::setDevice(device_id_);
    cudaSetDevice(device_id_);

    mean_mat_g_ = cv::cuda::GpuMat(
        net_input_height_, net_input_width_, CV_32FC3,
        cv::Scalar(descriptor_.mean_colors[0], descriptor_.mean_colors[1],
                   descriptor_.mean_colors[2]));

    frame_input_g_.clear();
    resized_input_g_.clear();
    float_input_g_.clear();
    normalized_input_g_.clear();
    input_planes_g_.clear();
    planar_input_g_.clear();
    flipped_planes_g_.clear();
    for (size_t i = 0; i < num_cuda_streams_; ++i) {
      frame_input_g_.push_back(
          cv::cuda::GpuMat(metadata_.height(), metadata_.width(), CV_8UC3));
      resized_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_8UC3));
      float_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      normalized_input_g_.push_back(
          cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC3));
      std::vector<cv::cuda::GpuMat> planes;
      std::vector<cv::cuda::GpuMat> flipped_planes;
      for (i32 i = 0; i < 3; ++i) {
        planes.push_back(
            cv::cuda::GpuMat(net_input_height_, net_input_width_, CV_32FC1));
        flipped_planes.push_back(
            cv::cuda::GpuMat(net_input_width_, net_input_height_, CV_32FC1));
      }
      input_planes_g_.push_back(planes);
      flipped_planes_g_.push_back(flipped_planes);
      planar_input_g_.push_back(
          cv::cuda::GpuMat(net_input_width_ * 3, net_input_height_, CV_32FC1));
    }
#else
    LOG(FATAL) << "Not built with CUDA support.";
#endif
  } else {
    mean_mat_c_ = cv::Mat(
        net_input_height_, net_input_width_, CV_32FC3,
        cv::Scalar(descriptor_.mean_colors[0], descriptor_.mean_colors[1],
                   descriptor_.mean_colors[2]));

    float_input_c_ = cv::Mat(net_input_height_, net_input_width_, CV_32FC3);
    normalized_input_c_ =
        cv::Mat(net_input_height_, net_input_width_, CV_32FC3);
    for (i32 i = 0; i < 3; ++i) {
      input_planes_c_.push_back(
          cv::Mat(net_input_height_, net_input_width_, CV_32FC1));
    }
    planar_input_c_ =
        cv::Mat(net_input_height_ * 3, net_input_width_, CV_32FC1);
  }
}

void FacenetInputEvaluator::evaluate(const BatchedColumns& input_columns,
                                     BatchedColumns& output_columns) {
  auto eval_start = now();

  size_t frame_size = net_input_width_ * net_input_height_ * 3;
  i32 input_count = input_columns[0].rows.size();
  i32 net_input_size = frame_size * sizeof(f32);

  u8* output_block = new_block_buffer({device_type_, device_id_},
                                      input_count * net_input_size,
                                      input_count);

  if (device_type_ == DeviceType::GPU) {
#ifdef HAVE_CUDA
    streams_.resize(0);
    streams_.resize(num_cuda_streams_);

    for (i32 i = 0; i < input_count; ++i) {
      int sid = i % num_cuda_streams_;
      cv::cuda::Stream& cv_stream = streams_[sid];

      f32* net_input = (f32*) (output_block + i * net_input_size);

      u8 *buffer = input_columns[0].rows[i].buffer;
      frame_input_g_[sid] = cv::cuda::GpuMat(
          metadata_.height(), metadata_.width(), CV_8UC3, buffer);
      cv::cuda::resize(frame_input_g_[sid], resized_input_g_[sid],
                       cv::Size(net_input_width_, net_input_height_), 0, 0,
                       cv::INTER_LINEAR, cv_stream);
      resized_input_g_[sid].convertTo(float_input_g_[sid], CV_32FC3, cv_stream);
      cv::cuda::subtract(float_input_g_[sid], mean_mat_g_,
                         normalized_input_g_[sid], cv::noArray(), -1,
                         cv_stream);
      // Changed from interleaved RGB to planar RGB
      cv::cuda::split(normalized_input_g_[sid], input_planes_g_[sid],
                      cv_stream);
      cv::cuda::transpose(input_planes_g_[sid][0], flipped_planes_g_[sid][0],
                          cv_stream);
      cv::cuda::transpose(input_planes_g_[sid][1], flipped_planes_g_[sid][1],
                          cv_stream);
      cv::cuda::transpose(input_planes_g_[sid][2], flipped_planes_g_[sid][2],
                          cv_stream);
      auto& plane1 = flipped_planes_g_[sid][0];
      auto& plane2 = flipped_planes_g_[sid][1];
      auto& plane3 = flipped_planes_g_[sid][2];
      auto& planar_input = planar_input_g_[sid];
      plane1.copyTo(planar_input(cv::Rect(
          0, net_input_width_ * 0, net_input_height_, net_input_width_)));
      plane2.copyTo(planar_input(cv::Rect(
          0, net_input_width_ * 1, net_input_height_, net_input_width_)));
      plane3.copyTo(planar_input(cv::Rect(
          0, net_input_width_ * 2, net_input_height_, net_input_width_)));
      assert(planar_input.cols == net_input_height_);
      cudaStream_t s = cv::cuda::StreamAccessor::getStream(cv_stream);
      CU_CHECK(cudaMemcpy2DAsync(
          net_input, net_input_height_ * sizeof(float),
          planar_input.data, planar_input.step,
          net_input_height_ * sizeof(float),
          net_input_width_ * 3, cudaMemcpyDeviceToDevice, s));

      INSERT_ROW(output_columns[1], (u8*)net_input, net_input_size);
    }
    for (cv::cuda::Stream& s : streams_) {
      s.waitForCompletion();
    }
#else
    LOG(FATAL) << "Not built with CUDA support.";
#endif
  } else {
    for (i32 i = 0; i < input_count; ++i) {
      u8* buffer = input_columns[0].rows[i].buffer;
      f32* net_input = reinterpret_cast<f32*>(new u8[frame_size * sizeof(f32)]);

      cv::Mat input_mat =
          cv::Mat(net_input_height_, net_input_width_, CV_8UC3, buffer);

      // Changed from interleaved RGB to planar RGB
      input_mat.convertTo(float_input_c_, CV_32FC3);
      cv::subtract(float_input_c_, mean_mat_c_, normalized_input_c_);
      cv::split(normalized_input_c_, input_planes_c_);
      cv::vconcat(input_planes_c_, planar_input_c_);
      assert(planar_input_c_.cols == net_input_height_);
      for (i32 r = 0; r < planar_input_c_.rows; ++r) {
        u8* mat_pos = planar_input_c_.data + r * planar_input_c_.step;
        u8* input_pos = reinterpret_cast<u8*>(
            net_input + i * (net_input_width_ * net_input_height_ * 3) +
            r * net_input_width_);
        std::memcpy(input_pos, mat_pos, planar_input_c_.cols * sizeof(float));
      }

      INSERT_ROW(output_columns[1], (u8*)net_input, frame_size * sizeof(f32));
    }
  }

  for (i32 i = 0; i < input_columns[0].rows.size(); ++i) {
    INSERT_ROW(output_columns[0], input_columns[0].rows[i].buffer,
               input_columns[0].rows[i].size);
  }

  if (profiler_) {
    profiler_->add_interval("facenet_input", eval_start, now());
  }
}

FacenetInputEvaluatorFactory::FacenetInputEvaluatorFactory(
    DeviceType device_type, const NetDescriptor& descriptor, i32 batch_size,
    f32 scale)
    : device_type_(device_type),
      net_descriptor_(descriptor),
      batch_size_(batch_size),
      scale_(scale) {}

EvaluatorCapabilities FacenetInputEvaluatorFactory::get_capabilities() {
  EvaluatorCapabilities caps;
  caps.device_type = device_type_;
  if (device_type_ == DeviceType::GPU) {
    caps.max_devices = 1;
  } else {
    caps.max_devices = EvaluatorCapabilities::UnlimitedDevices;
  }
  caps.warmup_size = 0;
  return caps;
}

std::vector<std::string> FacenetInputEvaluatorFactory::get_output_columns(
    const std::vector<std::string>& input_columns) {
  return {"frame", "net_input"};
}

Evaluator *
FacenetInputEvaluatorFactory::new_evaluator(const EvaluatorConfig &config) {
  return new FacenetInputEvaluator(device_type_, config.device_ids[0],
                                   net_descriptor_, batch_size_, scale_);
}
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: regactivex.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: kz $ $Date: 2005-01-18 15:03:03 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 ( the "License" ); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor( s ): _______________________________________
 *
 *
 ************************************************************************/

#define UNICODE

#include <windows.h>
#include <msiquery.h>
#include <string.h>
#include <malloc.h>

#define CHART_COMPONENT 1
#define DRAW_COMPONENT 2
#define IMPRESS_COMPONENT 4
#define CALC_COMPONENT 8
#define WRITER_COMPONENT 16
#define MATH_COMPONENT 32

typedef int ( __stdcall * DllNativeProc ) ( int, BOOL );

BOOL UnicodeEquals( wchar_t* pStr1, wchar_t* pStr2 )
{
    if ( pStr1 == NULL && pStr2 == NULL )
        return TRUE;
    else if ( pStr1 == NULL || pStr2 == NULL )
        return FALSE;

    while( *pStr1 == *pStr2 && *pStr1 && *pStr2 )
        pStr1++, pStr2++;

    return ( *pStr1 == 0 && *pStr2 == 0 );
}

//----------------------------------------------------------
char* UnicodeToAnsiString( wchar_t* pUniString )
{
    int len = WideCharToMultiByte(
        CP_ACP, 0, pUniString, -1, 0, 0, 0, 0 );

    char* buff = reinterpret_cast<char*>( malloc( len ) );

    WideCharToMultiByte(
        CP_ACP, 0, pUniString, -1, buff, len, 0, 0 );

    return buff;
}

//----------------------------------------------------------
void RegisterActiveXNative( const char* pActiveXPath, int nMode, BOOL InstallForAllUser )
{
    HINSTANCE hModule = LoadLibraryExA( pActiveXPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
    if( !( hModule <= ( HINSTANCE )HINSTANCE_ERROR ) )
    {
        DllNativeProc pNativeProc = ( DllNativeProc )GetProcAddress( hModule, "DllRegisterServerNative" );
        if( pNativeProc!=NULL )
            ( *pNativeProc )( nMode, InstallForAllUser );

        FreeLibrary( hModule );
    }
}

//----------------------------------------------------------
void UnregisterActiveXNative( const char* pActiveXPath, int nMode, BOOL InstallForAllUser )
{
    HINSTANCE hModule = LoadLibraryExA( pActiveXPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
    if( !( hModule <= ( HINSTANCE )HINSTANCE_ERROR ) )
    {
        DllNativeProc pNativeProc = ( DllNativeProc )GetProcAddress( hModule, "DllUnregisterServerNative" );
        if( pNativeProc!=NULL )
            ( *pNativeProc )( nMode, InstallForAllUser );

        FreeLibrary( hModule );
    }
}

//----------------------------------------------------------
BOOL GetMsiProp( MSIHANDLE hMSI, const wchar_t* pPropName, wchar_t** ppValue )
{
    DWORD sz = 0;
       if ( MsiGetProperty( hMSI, pPropName, L"", &sz ) == ERROR_MORE_DATA )
       {
           sz++;
           DWORD nbytes = sz * sizeof( wchar_t );
           wchar_t* buff = reinterpret_cast<wchar_t*>( malloc( nbytes ) );
           ZeroMemory( buff, nbytes );
           MsiGetProperty( hMSI, pPropName, buff, &sz );
           *ppValue = buff;

        return TRUE;
    }

    return FALSE;
}

//----------------------------------------------------------
BOOL GetActiveXControlPath( MSIHANDLE hMSI, char** ppActiveXPath )
{
    wchar_t* pProgPath = NULL;
    if ( GetMsiProp( hMSI, L"INSTALLLOCATION", &pProgPath ) && pProgPath )
       {
        char* pCharProgPath = UnicodeToAnsiString( pProgPath );
        if ( pCharProgPath )
        {
            int nLen = strlen( pCharProgPath );
            *ppActiveXPath = reinterpret_cast<char*>( malloc( nLen + 23 ) );
            strncpy( *ppActiveXPath, pCharProgPath, nLen );
            strncpy( (*ppActiveXPath) + nLen, "program\\so_activex.dll", 22 );
            (*ppActiveXPath)[nLen+22] = 0;

            free( pCharProgPath );

            return TRUE;
        }

        free( pProgPath );
    }

    return FALSE;
}

//----------------------------------------------------------
BOOL GetDelta( MSIHANDLE hMSI, int& nOldInstallMode, int& nInstallMode, int& nDeinstallMode )
{
    // for now the chart is always installed
    nOldInstallMode = CHART_COMPONENT;
    nInstallMode = CHART_COMPONENT;
    nDeinstallMode = 0;

    INSTALLSTATE current_state;
       INSTALLSTATE future_state;

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Wrt_Bin", &current_state, &future_state ) )
    {
        // analyze writer installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= WRITER_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= WRITER_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= WRITER_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Calc_Bin", &current_state, &future_state ) )
    {
        // analyze calc installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= CALC_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= CALC_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= CALC_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Draw_Bin", &current_state, &future_state ) )
    {
        // analyze draw installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= DRAW_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= DRAW_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= DRAW_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Impress_Bin", &current_state, &future_state ) )
    {
        // analyze impress installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= IMPRESS_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= IMPRESS_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= IMPRESS_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Math_Bin", &current_state, &future_state ) )
    {
        // analyze impress installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= MATH_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= MATH_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= MATH_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    return TRUE;
}

//----------------------------------------------------------
BOOL MakeInstallForAllUsers( MSIHANDLE hMSI )
{
    BOOL bResult = FALSE;
    wchar_t* pVal = NULL;
    if ( GetMsiProp( hMSI, L"ALLUSERS", &pVal ) && pVal )
    {
        bResult = UnicodeEquals( pVal , L"1" );
        free( pVal );
    }

    return bResult;
}

//----------------------------------------------------------
extern "C" UINT __stdcall InstallActiveXControl( MSIHANDLE hMSI )
{
    int nOldInstallMode = 0;
    int nInstallMode = 0;
    int nDeinstallMode = 0;

    // MessageBox(NULL, L"InstallActiveXControl", L"Information", MB_OK | MB_ICONINFORMATION);

    INSTALLSTATE current_state;
    INSTALLSTATE future_state;

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_o_Activexcontrol", &current_state, &future_state ) )
    {
        BOOL bInstallForAllUser = MakeInstallForAllUsers( hMSI );
        char* pActiveXPath = NULL;
        if ( GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath
        && GetDelta( hMSI, nOldInstallMode, nInstallMode, nDeinstallMode ) )
        {
            if ( future_state == INSTALLSTATE_LOCAL )
            {
                // the control is installed in the new selected configuration

                if ( current_state == INSTALLSTATE_LOCAL && nDeinstallMode )
                    UnregisterActiveXNative( pActiveXPath, nInstallMode, bInstallForAllUser );

                if ( nInstallMode )
                    RegisterActiveXNative( pActiveXPath, nInstallMode, bInstallForAllUser );
            }
            else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            {
                if ( nOldInstallMode )
                    UnregisterActiveXNative( pActiveXPath, nOldInstallMode, bInstallForAllUser );
            }
        }

        if ( pActiveXPath )
            free( pActiveXPath );
    }
    else
    {
        // assert( FALSE );
    }

    return ERROR_SUCCESS;
}

//----------------------------------------------------------
extern "C" __stdcall UINT DeinstallActiveXControl( MSIHANDLE hMSI )
{
    INSTALLSTATE current_state;
    INSTALLSTATE future_state;

    // MessageBox(NULL, L"DeinstallActiveXControl", L"Information", MB_OK | MB_ICONINFORMATION);

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_o_Activexcontrol", &current_state, &future_state ) )
    {
        char* pActiveXPath = NULL;
        if ( current_state == INSTALLSTATE_LOCAL && GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath )
        {
            BOOL bInstallForAllUser = MakeInstallForAllUsers( hMSI );

            wchar_t* rm = NULL;
            if ( GetMsiProp( hMSI, L"REMOVE", &rm ) && rm && UnicodeEquals( rm, L"ALL" ) )
            {
                UnregisterActiveXNative( pActiveXPath,
                                        CHART_COMPONENT
                                        | DRAW_COMPONENT
                                        | IMPRESS_COMPONENT
                                        | CALC_COMPONENT
                                        | WRITER_COMPONENT,
                                        bInstallForAllUser );
            }

            if ( rm )
                free( rm );

            free( pActiveXPath );
        }
    }

    return ERROR_SUCCESS;
}

<commit_msg>INTEGRATION: CWS winregclean1 (1.6.28); FILE MERGED 2005/04/25 10:25:40 mav 1.6.28.1: #i47658# for Win98/WinME registration is always done in local machine<commit_after>/*************************************************************************
 *
 *  $RCSfile: regactivex.cxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: obo $ $Date: 2005-04-29 08:53:32 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 ( the "License" ); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor( s ): _______________________________________
 *
 *
 ************************************************************************/

#define UNICODE

#include <windows.h>
#include <msiquery.h>
#include <string.h>
#include <malloc.h>

#define CHART_COMPONENT 1
#define DRAW_COMPONENT 2
#define IMPRESS_COMPONENT 4
#define CALC_COMPONENT 8
#define WRITER_COMPONENT 16
#define MATH_COMPONENT 32

typedef int ( __stdcall * DllNativeProc ) ( int, BOOL );

BOOL UnicodeEquals( wchar_t* pStr1, wchar_t* pStr2 )
{
    if ( pStr1 == NULL && pStr2 == NULL )
        return TRUE;
    else if ( pStr1 == NULL || pStr2 == NULL )
        return FALSE;

    while( *pStr1 == *pStr2 && *pStr1 && *pStr2 )
        pStr1++, pStr2++;

    return ( *pStr1 == 0 && *pStr2 == 0 );
}

//----------------------------------------------------------
char* UnicodeToAnsiString( wchar_t* pUniString )
{
    int len = WideCharToMultiByte(
        CP_ACP, 0, pUniString, -1, 0, 0, 0, 0 );

    char* buff = reinterpret_cast<char*>( malloc( len ) );

    WideCharToMultiByte(
        CP_ACP, 0, pUniString, -1, buff, len, 0, 0 );

    return buff;
}

//----------------------------------------------------------
void RegisterActiveXNative( const char* pActiveXPath, int nMode, BOOL InstallForAllUser )
{
    // For Win98/WinME the values should be written to the local machine
    OSVERSIONINFO       aVerInfo;
    aVerInfo.dwOSVersionInfoSize = sizeof( aVerInfo );
    if ( GetVersionEx( &aVerInfo ) && aVerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT )
        InstallForAllUser = TRUE;

    HINSTANCE hModule = LoadLibraryExA( pActiveXPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
    if( !( hModule <= ( HINSTANCE )HINSTANCE_ERROR ) )
    {
        DllNativeProc pNativeProc = ( DllNativeProc )GetProcAddress( hModule, "DllRegisterServerNative" );
        if( pNativeProc!=NULL )
            ( *pNativeProc )( nMode, InstallForAllUser );

        FreeLibrary( hModule );
    }
}

//----------------------------------------------------------
void UnregisterActiveXNative( const char* pActiveXPath, int nMode, BOOL InstallForAllUser )
{
    // For Win98/WinME the values should be written to the local machine
    OSVERSIONINFO       aVerInfo;
    aVerInfo.dwOSVersionInfoSize = sizeof( aVerInfo );
    if ( GetVersionEx( &aVerInfo ) && aVerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT )
        InstallForAllUser = TRUE;

    HINSTANCE hModule = LoadLibraryExA( pActiveXPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
    if( !( hModule <= ( HINSTANCE )HINSTANCE_ERROR ) )
    {
        DllNativeProc pNativeProc = ( DllNativeProc )GetProcAddress( hModule, "DllUnregisterServerNative" );
        if( pNativeProc!=NULL )
            ( *pNativeProc )( nMode, InstallForAllUser );

        FreeLibrary( hModule );
    }
}

//----------------------------------------------------------
BOOL GetMsiProp( MSIHANDLE hMSI, const wchar_t* pPropName, wchar_t** ppValue )
{
    DWORD sz = 0;
       if ( MsiGetProperty( hMSI, pPropName, L"", &sz ) == ERROR_MORE_DATA )
       {
           sz++;
           DWORD nbytes = sz * sizeof( wchar_t );
           wchar_t* buff = reinterpret_cast<wchar_t*>( malloc( nbytes ) );
           ZeroMemory( buff, nbytes );
           MsiGetProperty( hMSI, pPropName, buff, &sz );
           *ppValue = buff;

        return TRUE;
    }

    return FALSE;
}

//----------------------------------------------------------
BOOL GetActiveXControlPath( MSIHANDLE hMSI, char** ppActiveXPath )
{
    wchar_t* pProgPath = NULL;
    if ( GetMsiProp( hMSI, L"INSTALLLOCATION", &pProgPath ) && pProgPath )
       {
        char* pCharProgPath = UnicodeToAnsiString( pProgPath );
        if ( pCharProgPath )
        {
            int nLen = strlen( pCharProgPath );
            *ppActiveXPath = reinterpret_cast<char*>( malloc( nLen + 23 ) );
            strncpy( *ppActiveXPath, pCharProgPath, nLen );
            strncpy( (*ppActiveXPath) + nLen, "program\\so_activex.dll", 22 );
            (*ppActiveXPath)[nLen+22] = 0;

            free( pCharProgPath );

            return TRUE;
        }

        free( pProgPath );
    }

    return FALSE;
}

//----------------------------------------------------------
BOOL GetDelta( MSIHANDLE hMSI, int& nOldInstallMode, int& nInstallMode, int& nDeinstallMode )
{
    // for now the chart is always installed
    nOldInstallMode = CHART_COMPONENT;
    nInstallMode = CHART_COMPONENT;
    nDeinstallMode = 0;

    INSTALLSTATE current_state;
       INSTALLSTATE future_state;

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Wrt_Bin", &current_state, &future_state ) )
    {
        // analyze writer installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= WRITER_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= WRITER_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= WRITER_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Calc_Bin", &current_state, &future_state ) )
    {
        // analyze calc installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= CALC_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= CALC_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= CALC_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Draw_Bin", &current_state, &future_state ) )
    {
        // analyze draw installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= DRAW_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= DRAW_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= DRAW_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Impress_Bin", &current_state, &future_state ) )
    {
        // analyze impress installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= IMPRESS_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= IMPRESS_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= IMPRESS_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Math_Bin", &current_state, &future_state ) )
    {
        // analyze impress installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= MATH_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= MATH_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= MATH_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    return TRUE;
}

//----------------------------------------------------------
BOOL MakeInstallForAllUsers( MSIHANDLE hMSI )
{
    BOOL bResult = FALSE;
    wchar_t* pVal = NULL;
    if ( GetMsiProp( hMSI, L"ALLUSERS", &pVal ) && pVal )
    {
        bResult = UnicodeEquals( pVal , L"1" );
        free( pVal );
    }

    return bResult;
}

//----------------------------------------------------------
extern "C" UINT __stdcall InstallActiveXControl( MSIHANDLE hMSI )
{
    int nOldInstallMode = 0;
    int nInstallMode = 0;
    int nDeinstallMode = 0;

    // MessageBox(NULL, L"InstallActiveXControl", L"Information", MB_OK | MB_ICONINFORMATION);

    INSTALLSTATE current_state;
    INSTALLSTATE future_state;

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_o_Activexcontrol", &current_state, &future_state ) )
    {
        BOOL bInstallForAllUser = MakeInstallForAllUsers( hMSI );
        char* pActiveXPath = NULL;
        if ( GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath
        && GetDelta( hMSI, nOldInstallMode, nInstallMode, nDeinstallMode ) )
        {
            if ( future_state == INSTALLSTATE_LOCAL )
            {
                // the control is installed in the new selected configuration

                if ( current_state == INSTALLSTATE_LOCAL && nDeinstallMode )
                    UnregisterActiveXNative( pActiveXPath, nInstallMode, bInstallForAllUser );

                if ( nInstallMode )
                    RegisterActiveXNative( pActiveXPath, nInstallMode, bInstallForAllUser );
            }
            else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            {
                if ( nOldInstallMode )
                    UnregisterActiveXNative( pActiveXPath, nOldInstallMode, bInstallForAllUser );
            }
        }

        if ( pActiveXPath )
            free( pActiveXPath );
    }
    else
    {
        // assert( FALSE );
    }

    return ERROR_SUCCESS;
}

//----------------------------------------------------------
extern "C" __stdcall UINT DeinstallActiveXControl( MSIHANDLE hMSI )
{
    INSTALLSTATE current_state;
    INSTALLSTATE future_state;

    // MessageBox(NULL, L"DeinstallActiveXControl", L"Information", MB_OK | MB_ICONINFORMATION);

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_o_Activexcontrol", &current_state, &future_state ) )
    {
        char* pActiveXPath = NULL;
        if ( current_state == INSTALLSTATE_LOCAL && GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath )
        {
            BOOL bInstallForAllUser = MakeInstallForAllUsers( hMSI );

            wchar_t* rm = NULL;
            if ( GetMsiProp( hMSI, L"REMOVE", &rm ) && rm && UnicodeEquals( rm, L"ALL" ) )
            {
                UnregisterActiveXNative( pActiveXPath,
                                        CHART_COMPONENT
                                        | DRAW_COMPONENT
                                        | IMPRESS_COMPONENT
                                        | CALC_COMPONENT
                                        | WRITER_COMPONENT,
                                        bInstallForAllUser );
            }

            if ( rm )
                free( rm );

            free( pActiveXPath );
        }
    }

    return ERROR_SUCCESS;
}

<|endoftext|>


<commit_before>/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <memory>
#include <utility>

#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/Dialect/Linalg/Passes.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_dialect.h"
#include "tensorflow/compiler/mlir/tfrt/jit/transforms/tf_cpurt_passes.h"

namespace tensorflow {
namespace {

#define GEN_PASS_CLASSES
#include "tensorflow/compiler/mlir/tfrt/jit/transforms/tf_cpurt_passes.h.inc"

using llvm::makeArrayRef;
using mlir::BlockAndValueMapping;
using mlir::BlockArgument;
using mlir::cast;
using mlir::dyn_cast;
using mlir::failure;
using mlir::Identifier;
using mlir::Location;
using mlir::LogicalResult;
using mlir::MLIRContext;
using mlir::OpBuilder;
using mlir::Operation;
using mlir::OpRewritePattern;
using mlir::PatternRewriter;
using mlir::SmallVector;
using mlir::success;
using mlir::Value;
using mlir::ValueRange;
using mlir::linalg::FillOp;
using mlir::linalg::GenericOp;
using mlir::linalg::InitTensorOp;
using mlir::linalg::LinalgOp;
using mlir::linalg::TiledLoopOp;
using mlir::linalg::YieldOp;
using mlir::tensor::ExtractSliceOp;
using mlir::tensor::InsertSliceOp;

// Fuses `linalg.fill` into a loop with a tiled reduction.
// Currently, only 2D case is supported. Fusion into a tiled 1D reduction is
// also possible.
struct FuseFillIntoTiledReductionPattern : public OpRewritePattern<GenericOp> {
  explicit FuseFillIntoTiledReductionPattern(MLIRContext *context,
                                             mlir::PatternBenefit benefit = 1)
      : OpRewritePattern<GenericOp>(context, benefit) {}

  LogicalResult matchAndRewrite(GenericOp linalg_op,
                                PatternRewriter &rewriter) const override {
    if (linalg_op.getNumOutputs() != 1) return failure();
    if (linalg_op.getNumLoops() != 2) return failure();

    // Get immediate parent.
    auto tiled_loop_op =
        dyn_cast<TiledLoopOp>(linalg_op->getParentRegion()->getParentOp());
    if (!tiled_loop_op) return failure();
    if (tiled_loop_op.getNumLoops() != 2) return failure();

    return RewriteTiledReduction(rewriter, tiled_loop_op, linalg_op);
  }

 private:
  // Add a new output argument to the `tiled_loop`. It will be produced by
  // `init_tensor` op with the same shape as the first output argument.
  //
  // Rewrite
  //
  //   %init = linalg.init_tensor
  //   %fill = linalg.fill(%cst, %init)
  //   linalg.tiled_loop outs(%fill)
  //
  // into
  //
  //   %init = linalg.init_tensor
  //** %init_clone = linalg.init_tensor
  //   %fill = linalg.fill(%cst, %init)
  //** linalg.tiled_loop outs(%fill, %init_clone)
  BlockArgument CloneAndAppendInitTensorToTiledLoop(
      PatternRewriter &rewriter, FillOp fill, TiledLoopOp tiled_loop) const {
    OpBuilder::InsertionGuard guard(rewriter);
    rewriter.setInsertionPoint(fill);

    auto init = fill.output().getDefiningOp<InitTensorOp>();
    auto init_clone = cast<InitTensorOp>(rewriter.clone(*init));
    mlir::OpOperand *init_clone_output_operand;
    rewriter.updateRootInPlace(tiled_loop, [&]() {
      init_clone_output_operand =
          &tiled_loop.appendOutputOperand(rewriter, init_clone.result());
    });
    return tiled_loop.getTiedBlockArgument(*init_clone_output_operand);
  }

  // Fuse `fill` operation into the `tiled_loop`, rewire the `linalg.generic` to
  // use it as the output for the reduced tile. Also create an additional
  // `insert_slice` that updates the new output.
  //
  // Rewrite
  //
  // %init = linalg.init_tensor
  // %init_clone = linalg.init_tensor
  // %fill = linalg.fill(%cst, %init)
  // linalg.tiled_loop outs(%fill, %init_clone) {
  //   %extract_output_slice = tensor.extract_slice %fill
  //   %reduce = linalg.generic outs (%extract_output_slice)
  //   %insert_output_slice = tensor.insert_slice %reduce into %fill
  //   linalg.yield %insert_output_slice
  // }
  //
  // into
  //
  // %init = linalg.init_tensor
  // %init_clone = linalg.init_tensor
  // %fill = linalg.fill(%cst, %init)
  // linalg.tiled_loop outs(%fill, %init_clone) {
  //   %extract_output_slice = tensor.extract_slice %fill
  //
  //** %slice_of_cloned_output = tensor.extract_slice %init
  //** %fill_of_cloned_output = linalg.fill(%cst, %slice_of_cloned_output)
  //** %reduce = linalg.generic outs (%fill_of_cloned_output)
  //** %update_cloned_output = tensor.insert_slice %reduce into %init_clone
  //
  //   %insert_output_slice = tensor.insert_slice %reduce into %fill
  //   linalg.yield %insert_output_slice, %update_cloned_output
  // }
  void FuseFill(PatternRewriter &rewriter, LinalgOp tiled_op, FillOp fill,
                BlockArgument loop_output_bb_arg,
                BlockArgument cloned_output_bb_arg,
                ExtractSliceOp extract_output_slice,
                InsertSliceOp insert_output_slice) const {
    OpBuilder::InsertionGuard g(rewriter);
    rewriter.setInsertionPoint(tiled_op);

    BlockAndValueMapping bvm;
    bvm.map(loop_output_bb_arg, cloned_output_bb_arg);
    Value slice_of_cloned_output =
        cast<ExtractSliceOp>(rewriter.clone(*extract_output_slice, bvm));

    auto fused_fill = rewriter.create<FillOp>(tiled_op.getLoc(), fill.value(),
                                              slice_of_cloned_output);
    rewriter.updateRootInPlace(tiled_op, [&]() {
      tiled_op.getOutputOperand(0)->set(fused_fill.result());
    });

    bvm.map(insert_output_slice.dest(), cloned_output_bb_arg);
    rewriter.setInsertionPointAfter(tiled_op);
    Value cloned_insert =
        cast<InsertSliceOp>(rewriter.clone(*insert_output_slice, bvm));
    auto yield = tiled_op.getOperation()->getBlock()->getTerminator();
    rewriter.updateRootInPlace(
        yield, [&]() { yield->insertOperands(1, cloned_insert); });
  }

  // Add an operation that combines the partial result with the output.
  //
  // Rewrite
  //
  // %init = linalg.init_tensor
  // %init_clone = linalg.init_tensor
  // %fill = linalg.fill(%cst, %init)
  // linalg.tiled_loop outs(%fill, %init_clone) {
  //   %extract_output_slice = tensor.extract_slice %fill
  //
  //   %slice_of_cloned_output = tensor.extract_slice %init
  //   %fill_of_cloned_output = linalg.fill(%cst, %slice_of_cloned_output)
  //   %reduce = linalg.generic outs (%fill_of_cloned_output)
  //   %update_cloned_output = tensor.insert_slice %reduce into %init_clone
  //
  //   %insert_output_slice = tensor.insert_slice %reduce into %fill
  //   linalg.yield %insert_output_slice, %update_cloned_output
  // }
  //
  // into
  //
  // %init = linalg.init_tensor
  // %init_clone = linalg.init_tensor
  // %fill = linalg.fill(%cst, %init)
  // linalg.tiled_loop outs(%fill, %init_clone) {
  //   %extract_output_slice = tensor.extract_slice %fill
  //
  //   %slice_of_cloned_output = tensor.extract_slice %init
  //   %fill_of_cloned_output = linalg.fill(%cst, %slice_of_cloned_output)
  //   %reduce = linalg.generic outs (%fill_of_cloned_output)
  //   %update_cloned_output = tensor.insert_slice %reduce into %init_clone
  //
  //** %combine = linalg.generic ins (%reduce) outs (%extract_output_slice)
  //** %insert_output_slice = tensor.insert_slice %combine into %fill
  //
  //   linalg.yield %insert_output_slice, %update_cloned_output
  // }
  LogicalResult CombineReducedTileWithOutput(
      PatternRewriter &rewriter, LinalgOp tiled_op, Value partial_result,
      ExtractSliceOp extract_output_slice,
      InsertSliceOp insert_output_slice) const {
    rewriter.setInsertionPointAfter(tiled_op);
    auto num_parallel_loops = tiled_op.getNumParallelLoops();
    SmallVector<mlir::StringRef, 3> parallel_iter_types(
        num_parallel_loops, mlir::getParallelIteratorTypeName());
    auto id_map = rewriter.getMultiDimIdentityMap(num_parallel_loops);

    auto combiner_or = DetectCombiner(tiled_op);
    if (failed(combiner_or)) return failure();
    Operation *combiner = combiner_or.getValue();

    auto accumulator = rewriter.create<GenericOp>(
        tiled_op.getLoc(), partial_result.getType(),
        makeArrayRef(partial_result),
        makeArrayRef(extract_output_slice.result()),
        makeArrayRef({id_map, id_map}), parallel_iter_types,
        [&](OpBuilder &b, Location nested_loc, ValueRange args) {
          BlockAndValueMapping bvm;
          bvm.map(combiner->getOperands(), args);
          Value result_val = b.clone(*combiner, bvm)->getResult(0);
          b.create<YieldOp>(nested_loc, result_val);
        });

    rewriter.updateRootInPlace(insert_output_slice, [&]() {
      insert_output_slice.sourceMutable().assign(accumulator.getResult(0));
    });
    return success();
  }

  // Unfortunaly, there is no way to modify the results of the loop inplace. So
  // we have to replace it with a clone.
  TiledLoopOp CreateLoopWithUpdatedResults(PatternRewriter &rewriter,
                                           TiledLoopOp tiled_loop) const {
    auto loc = tiled_loop.getLoc();
    rewriter.setInsertionPoint(tiled_loop);
    auto new_loop = rewriter.create<TiledLoopOp>(
        loc, mlir::TypeRange(tiled_loop.outputs()), tiled_loop.getOperands(),
        tiled_loop->getAttrs());
    rewriter.inlineRegionBefore(tiled_loop.region(), new_loop.region(),
                                new_loop.region().begin());

    rewriter.replaceOp(tiled_loop, new_loop.getResult(0));
    return new_loop;
  }

  // Fuses FillOp producer of the output argument of the TiledLoopOp and inserts
  // an operation that accumulates the partial result, i.e. reduced tile, and
  // the current value of the output tile.
  LogicalResult RewriteTiledReduction(PatternRewriter &rewriter,
                                      TiledLoopOp tiled_loop,
                                      LinalgOp tiled_op) const {
    OpBuilder::InsertionGuard guard(rewriter);
    rewriter.setInsertionPointAfter(tiled_op);

    // Find tiled loop output operand and the corresponding block argument.
    mlir::OpOperand *loop_output_operand =
        tiled_loop.findOutputOperand(tiled_loop.outputs().front());
    BlockArgument loop_output_bb_arg =
        tiled_loop.getTiedBlockArgument(*loop_output_operand);

    // Find `linalg.fill` producer of the output.
    auto fill = loop_output_operand->get().getDefiningOp<FillOp>();
    if (!fill) return failure();

    // Find extract_slice/insert_slice pair used to RMW output.
    auto extract_output_slice =
        tiled_op.getOutputOperand(0)->get().getDefiningOp<ExtractSliceOp>();
    if (!extract_output_slice) return failure();

    Value tiled_op_result = tiled_op->getResult(0);
    auto insert_output_slice =
        dyn_cast<InsertSliceOp>(*tiled_op_result.getUsers().begin());
    if (!insert_output_slice) return failure();

    // Fuse the output.
    BlockArgument cloned_output_bb_arg =
        CloneAndAppendInitTensorToTiledLoop(rewriter, fill, tiled_loop);
    FuseFill(rewriter, tiled_op, fill, loop_output_bb_arg, cloned_output_bb_arg,
             extract_output_slice, insert_output_slice);
    if (mlir::failed(CombineReducedTileWithOutput(
            rewriter, tiled_op, tiled_op_result, extract_output_slice,
            insert_output_slice)))
      return failure();

    // Update the results.
    CreateLoopWithUpdatedResults(rewriter, tiled_loop);
    return success();
  }
};

struct FuseFillIntoTiledReductionPass
    : public FuseFillIntoTiledReductionBase<FuseFillIntoTiledReductionPass> {
  void runOnFunction() override {
    auto func = getFunction();
    auto context = func.getContext();

    mlir::RewritePatternSet patterns(context);
    patterns.insert<FuseFillIntoTiledReductionPattern>(context);
    (void)mlir::applyPatternsAndFoldGreedily(func, std::move(patterns));
  }
};

}  // namespace

std::unique_ptr<mlir::FunctionPass> CreateFuseFillIntoTiledReductionPass() {
  return std::make_unique<FuseFillIntoTiledReductionPass>();
}

}  // namespace tensorflow
<commit_msg>Always update the loop results after adding an output argument.<commit_after>/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <memory>
#include <utility>

#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/Dialect/Linalg/Passes.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_dialect.h"
#include "tensorflow/compiler/mlir/tfrt/jit/transforms/tf_cpurt_passes.h"

namespace tensorflow {
namespace {

#define GEN_PASS_CLASSES
#include "tensorflow/compiler/mlir/tfrt/jit/transforms/tf_cpurt_passes.h.inc"

using llvm::makeArrayRef;
using mlir::BlockAndValueMapping;
using mlir::BlockArgument;
using mlir::cast;
using mlir::dyn_cast;
using mlir::failure;
using mlir::Identifier;
using mlir::Location;
using mlir::LogicalResult;
using mlir::MLIRContext;
using mlir::OpBuilder;
using mlir::Operation;
using mlir::OpRewritePattern;
using mlir::PatternRewriter;
using mlir::SmallVector;
using mlir::success;
using mlir::Value;
using mlir::ValueRange;
using mlir::linalg::FillOp;
using mlir::linalg::GenericOp;
using mlir::linalg::InitTensorOp;
using mlir::linalg::LinalgOp;
using mlir::linalg::TiledLoopOp;
using mlir::linalg::YieldOp;
using mlir::tensor::ExtractSliceOp;
using mlir::tensor::InsertSliceOp;

// Fuses `linalg.fill` into a loop with a tiled reduction.
// Currently, only 2D case is supported. Fusion into a tiled 1D reduction is
// also possible.
struct FuseFillIntoTiledReductionPattern : public OpRewritePattern<GenericOp> {
  explicit FuseFillIntoTiledReductionPattern(MLIRContext *context,
                                             mlir::PatternBenefit benefit = 1)
      : OpRewritePattern<GenericOp>(context, benefit) {}

  LogicalResult matchAndRewrite(GenericOp linalg_op,
                                PatternRewriter &rewriter) const override {
    if (linalg_op.getNumOutputs() != 1) return failure();
    if (linalg_op.getNumLoops() != 2) return failure();

    // Get immediate parent.
    auto tiled_loop_op =
        dyn_cast<TiledLoopOp>(linalg_op->getParentRegion()->getParentOp());
    if (!tiled_loop_op) return failure();
    if (tiled_loop_op.getNumLoops() != 2) return failure();

    return RewriteTiledReduction(rewriter, tiled_loop_op, linalg_op);
  }

 private:
  // Add a new output argument to the `tiled_loop`. It will be produced by
  // `init_tensor` op with the same shape as the first output argument.
  //
  // Rewrite
  //
  //   %init = linalg.init_tensor
  //   %fill = linalg.fill(%cst, %init)
  //   linalg.tiled_loop outs(%fill)
  //
  // into
  //
  //   %init = linalg.init_tensor
  //** %init_clone = linalg.init_tensor
  //   %fill = linalg.fill(%cst, %init)
  //** linalg.tiled_loop outs(%fill, %init_clone)
  BlockArgument CloneAndAppendInitTensorToTiledLoop(
      PatternRewriter &rewriter, FillOp fill, TiledLoopOp tiled_loop) const {
    OpBuilder::InsertionGuard guard(rewriter);
    rewriter.setInsertionPoint(fill);

    auto init = fill.output().getDefiningOp<InitTensorOp>();
    auto init_clone = cast<InitTensorOp>(rewriter.clone(*init));
    mlir::OpOperand *init_clone_output_operand;
    rewriter.updateRootInPlace(tiled_loop, [&]() {
      init_clone_output_operand =
          &tiled_loop.appendOutputOperand(rewriter, init_clone.result());
    });
    return tiled_loop.getTiedBlockArgument(*init_clone_output_operand);
  }

  // Fuse `fill` operation into the `tiled_loop`, rewire the `linalg.generic` to
  // use it as the output for the reduced tile. Also create an additional
  // `insert_slice` that updates the new output.
  //
  // Rewrite
  //
  // %init = linalg.init_tensor
  // %init_clone = linalg.init_tensor
  // %fill = linalg.fill(%cst, %init)
  // linalg.tiled_loop outs(%fill, %init_clone) {
  //   %extract_output_slice = tensor.extract_slice %fill
  //   %reduce = linalg.generic outs (%extract_output_slice)
  //   %insert_output_slice = tensor.insert_slice %reduce into %fill
  //   linalg.yield %insert_output_slice
  // }
  //
  // into
  //
  // %init = linalg.init_tensor
  // %init_clone = linalg.init_tensor
  // %fill = linalg.fill(%cst, %init)
  // linalg.tiled_loop outs(%fill, %init_clone) {
  //   %extract_output_slice = tensor.extract_slice %fill
  //
  //** %slice_of_cloned_output = tensor.extract_slice %init
  //** %fill_of_cloned_output = linalg.fill(%cst, %slice_of_cloned_output)
  //** %reduce = linalg.generic outs (%fill_of_cloned_output)
  //** %update_cloned_output = tensor.insert_slice %reduce into %init_clone
  //
  //   %insert_output_slice = tensor.insert_slice %reduce into %fill
  //   linalg.yield %insert_output_slice, %update_cloned_output
  // }
  void FuseFill(PatternRewriter &rewriter, LinalgOp tiled_op, FillOp fill,
                BlockArgument loop_output_bb_arg,
                BlockArgument cloned_output_bb_arg,
                ExtractSliceOp extract_output_slice,
                InsertSliceOp insert_output_slice) const {
    OpBuilder::InsertionGuard g(rewriter);
    rewriter.setInsertionPoint(tiled_op);

    BlockAndValueMapping bvm;
    bvm.map(loop_output_bb_arg, cloned_output_bb_arg);
    Value slice_of_cloned_output =
        cast<ExtractSliceOp>(rewriter.clone(*extract_output_slice, bvm));

    auto fused_fill = rewriter.create<FillOp>(tiled_op.getLoc(), fill.value(),
                                              slice_of_cloned_output);
    rewriter.updateRootInPlace(tiled_op, [&]() {
      tiled_op.getOutputOperand(0)->set(fused_fill.result());
    });

    bvm.map(insert_output_slice.dest(), cloned_output_bb_arg);
    rewriter.setInsertionPointAfter(tiled_op);
    Value cloned_insert =
        cast<InsertSliceOp>(rewriter.clone(*insert_output_slice, bvm));
    auto yield = tiled_op.getOperation()->getBlock()->getTerminator();
    rewriter.updateRootInPlace(
        yield, [&]() { yield->insertOperands(1, cloned_insert); });
  }

  // Add an operation that combines the partial result with the output.
  //
  // Rewrite
  //
  // %init = linalg.init_tensor
  // %init_clone = linalg.init_tensor
  // %fill = linalg.fill(%cst, %init)
  // linalg.tiled_loop outs(%fill, %init_clone) {
  //   %extract_output_slice = tensor.extract_slice %fill
  //
  //   %slice_of_cloned_output = tensor.extract_slice %init
  //   %fill_of_cloned_output = linalg.fill(%cst, %slice_of_cloned_output)
  //   %reduce = linalg.generic outs (%fill_of_cloned_output)
  //   %update_cloned_output = tensor.insert_slice %reduce into %init_clone
  //
  //   %insert_output_slice = tensor.insert_slice %reduce into %fill
  //   linalg.yield %insert_output_slice, %update_cloned_output
  // }
  //
  // into
  //
  // %init = linalg.init_tensor
  // %init_clone = linalg.init_tensor
  // %fill = linalg.fill(%cst, %init)
  // linalg.tiled_loop outs(%fill, %init_clone) {
  //   %extract_output_slice = tensor.extract_slice %fill
  //
  //   %slice_of_cloned_output = tensor.extract_slice %init
  //   %fill_of_cloned_output = linalg.fill(%cst, %slice_of_cloned_output)
  //   %reduce = linalg.generic outs (%fill_of_cloned_output)
  //   %update_cloned_output = tensor.insert_slice %reduce into %init_clone
  //
  //** %combine = linalg.generic ins (%reduce) outs (%extract_output_slice)
  //** %insert_output_slice = tensor.insert_slice %combine into %fill
  //
  //   linalg.yield %insert_output_slice, %update_cloned_output
  // }
  LogicalResult CombineReducedTileWithOutput(
      PatternRewriter &rewriter, LinalgOp tiled_op, Value partial_result,
      ExtractSliceOp extract_output_slice,
      InsertSliceOp insert_output_slice) const {
    rewriter.setInsertionPointAfter(tiled_op);
    auto num_parallel_loops = tiled_op.getNumParallelLoops();
    SmallVector<mlir::StringRef, 3> parallel_iter_types(
        num_parallel_loops, mlir::getParallelIteratorTypeName());
    auto id_map = rewriter.getMultiDimIdentityMap(num_parallel_loops);

    auto combiner_or = DetectCombiner(tiled_op);
    if (failed(combiner_or)) return failure();
    Operation *combiner = combiner_or.getValue();

    auto accumulator = rewriter.create<GenericOp>(
        tiled_op.getLoc(), partial_result.getType(),
        makeArrayRef(partial_result),
        makeArrayRef(extract_output_slice.result()),
        makeArrayRef({id_map, id_map}), parallel_iter_types,
        [&](OpBuilder &b, Location nested_loc, ValueRange args) {
          BlockAndValueMapping bvm;
          bvm.map(combiner->getOperands(), args);
          Value result_val = b.clone(*combiner, bvm)->getResult(0);
          b.create<YieldOp>(nested_loc, result_val);
        });

    rewriter.updateRootInPlace(insert_output_slice, [&]() {
      insert_output_slice.sourceMutable().assign(accumulator.getResult(0));
    });
    return success();
  }

  // Unfortunaly, there is no way to modify the results of the loop inplace. So
  // we have to replace it with a clone.
  TiledLoopOp CreateLoopWithUpdatedResults(PatternRewriter &rewriter,
                                           TiledLoopOp tiled_loop) const {
    auto loc = tiled_loop.getLoc();
    rewriter.setInsertionPoint(tiled_loop);
    auto new_loop = rewriter.create<TiledLoopOp>(
        loc, mlir::TypeRange(tiled_loop.outputs()), tiled_loop.getOperands(),
        tiled_loop->getAttrs());
    rewriter.inlineRegionBefore(tiled_loop.region(), new_loop.region(),
                                new_loop.region().begin());

    rewriter.replaceOp(tiled_loop, new_loop.getResult(0));
    return new_loop;
  }

  // Fuses FillOp producer of the output argument of the TiledLoopOp and inserts
  // an operation that accumulates the partial result, i.e. reduced tile, and
  // the current value of the output tile.
  LogicalResult RewriteTiledReduction(PatternRewriter &rewriter,
                                      TiledLoopOp tiled_loop,
                                      LinalgOp tiled_op) const {
    OpBuilder::InsertionGuard guard(rewriter);
    rewriter.setInsertionPointAfter(tiled_op);

    // Find tiled loop output operand and the corresponding block argument.
    mlir::OpOperand *loop_output_operand =
        tiled_loop.findOutputOperand(tiled_loop.outputs().front());
    BlockArgument loop_output_bb_arg =
        tiled_loop.getTiedBlockArgument(*loop_output_operand);

    // Find `linalg.fill` producer of the output.
    auto fill = loop_output_operand->get().getDefiningOp<FillOp>();
    if (!fill) return failure();

    // Find extract_slice/insert_slice pair used to RMW output.
    auto extract_output_slice =
        tiled_op.getOutputOperand(0)->get().getDefiningOp<ExtractSliceOp>();
    if (!extract_output_slice) return failure();

    Value tiled_op_result = tiled_op->getResult(0);
    auto insert_output_slice =
        dyn_cast<InsertSliceOp>(*tiled_op_result.getUsers().begin());
    if (!insert_output_slice) return failure();

    // Fuse the output.
    BlockArgument cloned_output_bb_arg =
        CloneAndAppendInitTensorToTiledLoop(rewriter, fill, tiled_loop);
    FuseFill(rewriter, tiled_op, fill, loop_output_bb_arg, cloned_output_bb_arg,
             extract_output_slice, insert_output_slice);
    // We have already modified the loop above, so we need to update the
    // results.
    CreateLoopWithUpdatedResults(rewriter, tiled_loop);
    return CombineReducedTileWithOutput(rewriter, tiled_op, tiled_op_result,
                                        extract_output_slice,
                                        insert_output_slice);
  }
};

struct FuseFillIntoTiledReductionPass
    : public FuseFillIntoTiledReductionBase<FuseFillIntoTiledReductionPass> {
  void runOnFunction() override {
    auto func = getFunction();
    auto context = func.getContext();

    mlir::RewritePatternSet patterns(context);
    patterns.insert<FuseFillIntoTiledReductionPattern>(context);
    (void)mlir::applyPatternsAndFoldGreedily(func, std::move(patterns));
  }
};

}  // namespace

std::unique_ptr<mlir::FunctionPass> CreateFuseFillIntoTiledReductionPass() {
  return std::make_unique<FuseFillIntoTiledReductionPass>();
}

}  // namespace tensorflow
<|endoftext|>