<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkMPIGroup.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

#include "vtkMPIGroup.h"
#include "vtkMPIController.h"
#include "vtkObjectFactory.h"

vtkCxxRevisionMacro(vtkMPIGroup, "1.4");
vtkStandardNewMacro(vtkMPIGroup);

void vtkMPIGroup::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "Initialized : ";
  if (this->Initialized)
    {
    os << "(yes)" << endl;
    }
  else
    {
    os << "(no)" << endl;
    }
  os << indent << "Maximum number of processe ids: "
     << this->MaximumNumberOfProcessIds << endl;
  os << indent << "First available position: "
     << this->CurrentPosition << endl;
  for(int i=0; i < this->CurrentPosition; i++)
    {
    os << indent << "Process id at " << i << " is "
       << this->ProcessIds[i]  << endl;
    }
  return;
}

vtkMPIGroup::vtkMPIGroup()
{
  this->ProcessIds = 0;
  this->MaximumNumberOfProcessIds = 0;
  this->CurrentPosition = 0;
  this->Initialized = 0;
}

vtkMPIGroup::~vtkMPIGroup()
{
  delete[] this->ProcessIds;
  this->MaximumNumberOfProcessIds = 0;
}

void vtkMPIGroup::Initialize(int numProcIds)
{
  if (this->Initialized)
    {
    return;
    }

  if (this->ProcessIds)
    {
    delete[] this->ProcessIds;
    }

  this->MaximumNumberOfProcessIds = numProcIds;
  if ( this->MaximumNumberOfProcessIds > 0 )
    {
    this->ProcessIds = new int[this->MaximumNumberOfProcessIds];
    }
  else
    {
    return;
    }

  this->Initialized = 1;
  this->Modified();
  return;
}

void vtkMPIGroup::Initialize(vtkMPIController* controller)
{
  this->Initialize(controller->GetNumberOfProcesses());
  return;
}

int vtkMPIGroup::AddProcessId(int processId)
{
  if ( this->CurrentPosition >= this->MaximumNumberOfProcessIds )
    {
    vtkWarningMacro("Can not add any more process ids. The group is full.");
    return 0;
    }
  if ( processId >= this->MaximumNumberOfProcessIds )
    {
    vtkWarningMacro("Process id " << processId << " is not valid.");
    return 0;
    }
  if ( this->FindProcessId(processId) >= 0 )
    {
    vtkWarningMacro("This process id is already in the group.");
    return 0;
    }
  else
    {
    this->ProcessIds[this->CurrentPosition] = processId;
    return ++this->CurrentPosition;
    }

  this->Modified();
}

void vtkMPIGroup::RemoveProcessId(int processId)
{
  int pos = this->FindProcessId(processId);
  if ( pos >= 0 )
    {
    for(int i=pos; i < this->CurrentPosition - 1; i++)
      {
      this->ProcessIds[i] = this->ProcessIds[i+1];
      }
    this->CurrentPosition--;
    this->Modified();
    }
  return;
}

int vtkMPIGroup::FindProcessId(int processId)
{
  for (int i=0; i < this->CurrentPosition; i++)
    {
    if ( this->ProcessIds[i] == processId )
      {
      return i;
      }
    }
  return -1;
}

int vtkMPIGroup::GetProcessId(int pos)
{
  if ( pos >= this->CurrentPosition)
    {
    return -1;
    }
  return this->ProcessIds[pos];
}

void vtkMPIGroup::CopyProcessIdsFrom(vtkMPIGroup* group)
{
  int max;
  // Find which MaximumNumberOfProcessIds is smallest and use that.
  if ( this->MaximumNumberOfProcessIds > group->MaximumNumberOfProcessIds )
    {
    max = group->MaximumNumberOfProcessIds;
    }
  else
    {
    max = this->MaximumNumberOfProcessIds;
    }
  // Copy.
  for(int i=0; i < max; i++)
    {
    this->ProcessIds[i] = group->ProcessIds[i];
    }

  // If the current position of group is smaller than
  // this->MaximumNumberOfProcessIds use it.
  if ( group->CurrentPosition <= this->MaximumNumberOfProcessIds)
    {
    this->CurrentPosition = group->CurrentPosition;
    }
  else
    {
    this->CurrentPosition = this->MaximumNumberOfProcessIds;
    }

  this->Modified();
  return;
}

void vtkMPIGroup::CopyFrom(vtkMPIGroup* source)
{
  this->Initialized = 0;
  this->Initialize(source->MaximumNumberOfProcessIds);
  this->CopyProcessIdsFrom(source);
}
<commit_msg>Fixed warning<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkMPIGroup.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

#include "vtkMPIGroup.h"
#include "vtkMPIController.h"
#include "vtkObjectFactory.h"

vtkCxxRevisionMacro(vtkMPIGroup, "1.5");
vtkStandardNewMacro(vtkMPIGroup);

void vtkMPIGroup::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "Initialized : ";
  if (this->Initialized)
    {
    os << "(yes)" << endl;
    }
  else
    {
    os << "(no)" << endl;
    }
  os << indent << "Maximum number of processe ids: "
     << this->MaximumNumberOfProcessIds << endl;
  os << indent << "First available position: "
     << this->CurrentPosition << endl;
  for(int i=0; i < this->CurrentPosition; i++)
    {
    os << indent << "Process id at " << i << " is "
       << this->ProcessIds[i]  << endl;
    }
  return;
}

vtkMPIGroup::vtkMPIGroup()
{
  this->ProcessIds = 0;
  this->MaximumNumberOfProcessIds = 0;
  this->CurrentPosition = 0;
  this->Initialized = 0;
}

vtkMPIGroup::~vtkMPIGroup()
{
  delete[] this->ProcessIds;
  this->MaximumNumberOfProcessIds = 0;
}

void vtkMPIGroup::Initialize(int numProcIds)
{
  if (this->Initialized)
    {
    return;
    }

  if (this->ProcessIds)
    {
    delete[] this->ProcessIds;
    }

  this->MaximumNumberOfProcessIds = numProcIds;
  if ( this->MaximumNumberOfProcessIds > 0 )
    {
    this->ProcessIds = new int[this->MaximumNumberOfProcessIds];
    }
  else
    {
    return;
    }

  this->Initialized = 1;
  this->Modified();
  return;
}

void vtkMPIGroup::Initialize(vtkMPIController* controller)
{
  this->Initialize(controller->GetNumberOfProcesses());
  return;
}

int vtkMPIGroup::AddProcessId(int processId)
{
  if ( this->CurrentPosition >= this->MaximumNumberOfProcessIds )
    {
    vtkWarningMacro("Can not add any more process ids. The group is full.");
    return 0;
    }
  if ( processId >= this->MaximumNumberOfProcessIds )
    {
    vtkWarningMacro("Process id " << processId << " is not valid.");
    return 0;
    }
  if ( this->FindProcessId(processId) >= 0 )
    {
    vtkWarningMacro("This process id is already in the group.");
    return 0;
    }

  this->ProcessIds[this->CurrentPosition] = processId;
  this->Modified();
  return ++this->CurrentPosition;
}

void vtkMPIGroup::RemoveProcessId(int processId)
{
  int pos = this->FindProcessId(processId);
  if ( pos >= 0 )
    {
    for(int i=pos; i < this->CurrentPosition - 1; i++)
      {
      this->ProcessIds[i] = this->ProcessIds[i+1];
      }
    this->CurrentPosition--;
    this->Modified();
    }
  return;
}

int vtkMPIGroup::FindProcessId(int processId)
{
  for (int i=0; i < this->CurrentPosition; i++)
    {
    if ( this->ProcessIds[i] == processId )
      {
      return i;
      }
    }
  return -1;
}

int vtkMPIGroup::GetProcessId(int pos)
{
  if ( pos >= this->CurrentPosition)
    {
    return -1;
    }
  return this->ProcessIds[pos];
}

void vtkMPIGroup::CopyProcessIdsFrom(vtkMPIGroup* group)
{
  int max;
  // Find which MaximumNumberOfProcessIds is smallest and use that.
  if ( this->MaximumNumberOfProcessIds > group->MaximumNumberOfProcessIds )
    {
    max = group->MaximumNumberOfProcessIds;
    }
  else
    {
    max = this->MaximumNumberOfProcessIds;
    }
  // Copy.
  for(int i=0; i < max; i++)
    {
    this->ProcessIds[i] = group->ProcessIds[i];
    }

  // If the current position of group is smaller than
  // this->MaximumNumberOfProcessIds use it.
  if ( group->CurrentPosition <= this->MaximumNumberOfProcessIds)
    {
    this->CurrentPosition = group->CurrentPosition;
    }
  else
    {
    this->CurrentPosition = this->MaximumNumberOfProcessIds;
    }

  this->Modified();
  return;
}

void vtkMPIGroup::CopyFrom(vtkMPIGroup* source)
{
  this->Initialized = 0;
  this->Initialize(source->MaximumNumberOfProcessIds);
  this->CopyProcessIdsFrom(source);
}
<|endoftext|>


<commit_before>#pragma once

#include <atomic>
#include <functional>
#include <memory>

#include "lw/error.hpp"

namespace lw {
namespace event {

LW_DEFINE_EXCEPTION( PromiseError );

// -------------------------------------------------------------------------- //

template< typename T >
class Future;

// -------------------------------------------------------------------------- //

/// @brief  Determines if the given variable is a `Future`, or derives publicly
///         from `Future`.
///
/// @tparam T The type to check.
template< typename T >
struct IsFuture : public std::integral_constant< bool, false >{};

template< typename T >
struct IsFuture< Future< T > > : public std::integral_constant< bool, true >{};

template< template< typename > class T, typename Value >
struct IsFuture< T< Value > > :
    public std::integral_constant<
        bool,
        std::is_base_of< Future< Value >, T< Value > >::value
    >
{};

// -------------------------------------------------------------------------- //

/// @brief `Promise`s and `Future`s allow for chaining callbacks without nesting.
///
/// A `Promise` is the active side of the pair. Asynchronous functions create a
/// promise and later fulfill it by either resolving or rejecting the promise.
template< typename T = void >
class Promise {
public:
    /// @brief Default construction.
    Promise( void ):
        m_state( new _SharedState() )
    {
        m_state->resolved   = false;
        m_state->rejected   = false;
        m_state->resolve    = nullptr;
        m_state->reject     = nullptr;
    }

    // ---------------------------------------------------------------------- //

    /// @brief No copying!
    Promise( const Promise& ) = delete;

    // ---------------------------------------------------------------------- //

    /// @brief Moves the promise from `other` to `this`.
    Promise( Promise&& other ):
        m_state( std::move( other.m_state ) )
    {
        other.m_state = nullptr;
    }

    // ---------------------------------------------------------------------- //

    /// @brief Returns a future associated with this promise.
    Future< T > future( void );

    // ---------------------------------------------------------------------- //

    /// @brief Resolves the promise as a success.
    void resolve( T&& value ){
        m_state->resolved = true;
        if( m_state->resolve ){
            m_state->resolve( std::move( value ) );
        }
    }

    // ---------------------------------------------------------------------- //

    /// @copydoc Promise::resolve(T&&)
    void resolve( const T& value ){
        resolve( T( value ) );
    }

    // ---------------------------------------------------------------------- //

    /// @brief Rejects the promise as a failure.
    void reject( const error::Exception& err ){
        m_state->rejected = true;
        if( m_state->reject ){
            m_state->reject( err );
        }
        else {
            throw err;
        }
    }

    // ---------------------------------------------------------------------- //

    /// @brief Resets the promise's internal state so that it can be reused.
    ///
    /// @throws PromiseError If the promise is in an unfinished state.
    void reset( void ){
        if( !is_finished() ){
            throw PromiseError( 1, "Cannot reset an unfinished promise." );
        }
        m_state->resolved   = false;
        m_state->rejected   = false;
        m_state->resolve    = nullptr;
        m_state->reject     = nullptr;
    }

    // ---------------------------------------------------------------------- //

    /// @brief Indicates if the promise has been resolved.
    bool is_resolved( void ) const {
        return m_state->resolved;
    }

    // ---------------------------------------------------------------------- //

    /// @brief Indicates if the promise has been rejected.
    bool is_rejected( void ) const {
        return m_state->rejected;
    }

    // ---------------------------------------------------------------------- //

    /// @brief Indicates if the promise has been either resolved or rejected.
    bool is_finished( void ) const {
        return is_resolved() || is_rejected();
    }

    // ---------------------------------------------------------------------- //

    /// @brief No copying!
    Promise& operator=( const Promise& ) = delete;

    // ---------------------------------------------------------------------- //

    /// @brief Moves the promise from `other` into `this`.
    Promise& operator=( Promise&& other ){
        m_state = std::move( other.m_state );
        other.m_state = nullptr;
        return *this;
    }

private:
    template< typename Type >
    friend class ::lw::event::Future;

    // ---------------------------------------------------------------------- //

    /// @brief The container for the shared state between promises and futures.
    struct _SharedState {
        std::atomic_bool resolved;
        std::atomic_bool rejected;
        std::function< void( T&& ) > resolve;
        std::function< void( const error::Exception& ) > reject;
    };
    typedef std::shared_ptr< _SharedState > _SharedStatePtr;

    // ---------------------------------------------------------------------- //

    /// @brief The state of the promise.
    _SharedStatePtr m_state;
};

// -------------------------------------------------------------------------- //

/// @brief The passive half of the `Promise`-`Future` pair.
///
/// `Future`s are the requester's handle on an asynchronous event. They allow
/// callbacks to be registered for after it has been started.
template< typename T = void >
class Future {
public:
    /// @brief The type promised by this future.
    typedef T result_type;

    /// @brief The type of Promise that made this future.
    typedef Promise< T > promise_type;

    // ---------------------------------------------------------------------- //

    template< typename Func >
    auto then( Func&& func ){
        return then( std::forward< Func >( func ), nullptr );
    }

    // ---------------------------------------------------------------------- //

    template< typename Result, typename Func >
    auto then( Func&& func ){
        return then< Result >( std::forward< Func >( func ), nullptr );
    }

    // ---------------------------------------------------------------------- //

    template< typename Resolve, typename Reject >
    auto then( Resolve&& resolve, Reject&& reject ){
        return _then(
            std::forward< Resolve   >( resolve  ),
            std::forward< Reject    >( reject   )
        );
    }

    // ---------------------------------------------------------------------- //

    template< typename Result, typename Resolve, typename Reject >
    auto then( Resolve&& resolve, Reject&& reject ){
        return _then< Result >(
            std::forward< Resolve   >( resolve  ),
            std::forward< Reject    >( reject   )
        );
    }

    // ---------------------------------------------------------------------- //

    /// @brief Connects this promise to the one provided.
    ///
    /// @param promise The promise to resolve/reject with this one.
    void then( promise_type&& promise );

    // ---------------------------------------------------------------------- //

private:
    template< typename Type >
    friend class ::lw::event::Promise;

    // ---------------------------------------------------------------------- //

    /// @brief Only `Promise`s can construct us.
    ///
    /// @param state The shared state to associate with.
    Future( const typename promise_type::_SharedStatePtr& state ):
        m_state( state )
    {}

    // ---------------------------------------------------------------------- //

    /// @brief Chaining for generic functors.
    ///
    /// @tparam Result  The type given functor promises.
    /// @tparam Resolve A functor type that can take a `Promise&&` as its parameter.
    ///
    /// @param resolve The functor to call when this one is resolved.
    ///
    /// @return A new future, for when the provided `resolve` completes its action.
    template<
        typename Result,
        typename Resolve,
        typename Reject,
        typename = typename std::result_of< Resolve( T&&, Promise< Result >&& ) >::type
    >
    Future< Result > _then( Resolve&& resolve, Reject&& reject );

    // ---------------------------------------------------------------------- //

    /// @brief Chaining for generic functors promising nothing.
    ///
    /// @tparam Resolve A functor type that can take a `Promise&&` as its parameter.
    ///
    /// @param resolve The functor to call when this one is resolved.
    ///
    /// @return A new future, for when the provided `resolve` completes its action.
    template<
        typename Resolve,
        typename Reject,
        typename = typename std::result_of< Resolve( T&&, Promise<>&& ) >::type
    >
    Future<> _then( Resolve&& resolve, Reject&& reject );

    // ---------------------------------------------------------------------- //

    /// @brief Chaining for `Future`-returning functors.
    ///
    /// @tparam Resolve A functor which returns a `Future`.
    ///
    /// @param resolve The functor to call when this one is ready.
    ///
    /// @return A `Future` which will be resolved by `resolve`.
    template<
        typename Resolve,
        typename Reject,
        typename ResolveResult = typename std::result_of< Resolve( T&& ) >::type,
        typename std::enable_if< IsFuture< ResolveResult >::value >::type* = nullptr
    >
    Future< typename ResolveResult::result_type > _then( Resolve&& resolve, Reject&& reject );

    // ---------------------------------------------------------------------- //

    /// @brief Chaining for value-returning functors (i.e. synchronous ones).
    ///
    /// @tparam Resolve A synchronous functor type.
    ///
    /// @param resolve A synchronous functor returning some value.
    ///
    /// @return A `Future` which will be resolved with the return value from `resolve`.
    template<
        typename Resolve,
        typename Reject,
        typename ResolveResult = typename std::result_of< Resolve( T&& ) >::type,
        typename std::enable_if<
            !IsFuture< ResolveResult >::value &&
            !std::is_void< ResolveResult >::value
        >::type* = nullptr
    >
    Future< ResolveResult > _then( Resolve&& resolve, Reject&& reject );

    // ---------------------------------------------------------------------- //

    /// @brief Chaining for void-returning synchronous functors.
    ///
    /// @tparam Resolve A synchronous functor type.
    ///
    /// @param resolve A synchronous functor with no return value.
    ///
    /// @return A `Future` which will be resolved `resolve` runs.
    template<
        typename Resolve,
        typename Reject,
        typename ResolveResult = typename std::result_of< Resolve( T&& ) >::type,
        typename std::enable_if< std::is_void< ResolveResult >::value >::type* = nullptr
    >
    Future<> _then( Resolve&& resolve, Reject&& reject );

    // ---------------------------------------------------------------------- //

    /// @brief Our internal shared state.
    typename promise_type::_SharedStatePtr m_state;
};

}
}
<commit_msg>Formatting update<commit_after>#pragma once

#include <atomic>
#include <functional>
#include <memory>

#include "lw/error.hpp"

namespace lw {
namespace event {

LW_DEFINE_EXCEPTION(PromiseError);

// ---------------------------------------------------------------------------------------------- //

template<typename T>
class Future;

// ---------------------------------------------------------------------------------------------- //

/// @brief Determines if the given variable is a `Future`, or derives publicly from `Future`.
///
/// @tparam T The type to check.
template<typename T>
struct IsFuture : public std::integral_constant<bool, false>{};

template<typename T>
struct IsFuture<Future<T>> : public std::integral_constant<bool, true>{};

template<template<typename> class T, typename Value>
struct IsFuture<T<Value>> :
    public std::integral_constant<bool, std::is_base_of<Future<Value>, T<Value>>::value>
{};

// ---------------------------------------------------------------------------------------------- //

/// @brief `Promise`s and `Future`s allow for chaining callbacks without nesting.
///
/// A `Promise` is the active side of the pair. Asynchronous functions create a promise and later
/// fulfill it by either resolving or rejecting the promise.
template<typename T = void>
class Promise {
public:
    /// @brief Default construction.
    Promise(void):
        m_state(new _SharedState())
    {
        m_state->resolved   = false;
        m_state->rejected   = false;
        m_state->resolve    = nullptr;
        m_state->reject     = nullptr;
    }

    // ------------------------------------------------------------------------------------------ //

    /// @brief No copying!
    Promise(const Promise&) = delete;

    // ------------------------------------------------------------------------------------------ //

    /// @brief Moves the promise from `other` to `this`.
    Promise(Promise&& other):
        m_state(std::move(other.m_state))
    {
        other.m_state = nullptr;
    }

    // ------------------------------------------------------------------------------------------ //

    /// @brief Returns a future associated with this promise.
    Future<T> future(void);

    // ------------------------------------------------------------------------------------------ //

    /// @brief Resolves the promise as a success.
    void resolve(T&& value){
        m_state->resolved = true;
        if (m_state->resolve) {
            m_state->resolve( std::move( value ) );
        }
    }

    // ------------------------------------------------------------------------------------------ //

    /// @copydoc Promise::resolve(T&&)
    void resolve(const T& value){
        resolve(T(value));
    }

    // ------------------------------------------------------------------------------------------ //

    /// @brief Rejects the promise as a failure.
    void reject(const error::Exception& err){
        m_state->rejected = true;
        if (m_state->reject) {
            m_state->reject(err);
        }
        else {
            throw err;
        }
    }

    // ------------------------------------------------------------------------------------------ //

    /// @brief Resets the promise's internal state so that it can be reused.
    ///
    /// @throws PromiseError If the promise is in an unfinished state.
    void reset(void){
        if (!is_finished()) {
            throw PromiseError(1, "Cannot reset an unfinished promise.");
        }
        m_state->resolved   = false;
        m_state->rejected   = false;
        m_state->resolve    = nullptr;
        m_state->reject     = nullptr;
    }

    // ------------------------------------------------------------------------------------------ //

    /// @brief Indicates if the promise has been resolved.
    bool is_resolved(void) const {
        return m_state->resolved;
    }

    // ------------------------------------------------------------------------------------------ //

    /// @brief Indicates if the promise has been rejected.
    bool is_rejected(void) const {
        return m_state->rejected;
    }

    // ------------------------------------------------------------------------------------------ //

    /// @brief Indicates if the promise has been either resolved or rejected.
    bool is_finished(void) const {
        return is_resolved() || is_rejected();
    }

    // ------------------------------------------------------------------------------------------ //

    /// @brief No copying!
    Promise& operator=(const Promise&) = delete;

    // ------------------------------------------------------------------------------------------ //

    /// @brief Moves the promise from `other` into `this`.
    Promise& operator=(Promise&& other){
        m_state = std::move(other.m_state);
        other.m_state = nullptr;
        return *this;
    }

private:
    template<typename Type>
    friend class ::lw::event::Future;

    // ------------------------------------------------------------------------------------------ //

    /// @brief The container for the shared state between promises and futures.
    struct _SharedState {
        std::atomic_bool resolved;
        std::atomic_bool rejected;
        std::function<void(T&&)> resolve;
        std::function<void(const error::Exception&)> reject;
    };
    typedef std::shared_ptr<_SharedState> _SharedStatePtr;

    // ------------------------------------------------------------------------------------------ //

    /// @brief The state of the promise.
    _SharedStatePtr m_state;
};

// ---------------------------------------------------------------------------------------------- //

/// @brief The passive half of the `Promise`-`Future` pair.
///
/// `Future`s are the requester's handle on an asynchronous event. They allow callbacks to be
/// registered for after it has been started.
template<typename T = void>
class Future {
public:
    /// @brief The type promised by this future.
    typedef T result_type;

    /// @brief The type of Promise that made this future.
    typedef Promise<T> promise_type;

    // ------------------------------------------------------------------------------------------ //

    template<typename Func>
    auto then(Func&& func){
        return then(std::forward<Func>(func), nullptr);
    }

    // ------------------------------------------------------------------------------------------ //

    template<typename Result, typename Func>
    auto then(Func&& func){
        return then<Result>(std::forward<Func>(func), nullptr);
    }

    // ------------------------------------------------------------------------------------------ //

    template<typename Resolve, typename Reject>
    auto then(Resolve&& resolve, Reject&& reject){
        return _then(std::forward<Resolve>(resolve), std::forward<Reject>(reject));
    }

    // ------------------------------------------------------------------------------------------ //

    template<typename Result, typename Resolve, typename Reject>
    auto then(Resolve&& resolve, Reject&& reject){
        return _then<Result>(std::forward<Resolve>(resolve), std::forward<Reject>(reject));
    }

    // ------------------------------------------------------------------------------------------ //

    /// @brief Connects this promise to the one provided.
    ///
    /// @param promise The promise to resolve/reject with this one.
    void then(promise_type&& promise);

    // ------------------------------------------------------------------------------------------ //

private:
    template<typename Type>
    friend class ::lw::event::Promise;

    // ------------------------------------------------------------------------------------------ //

    /// @brief Only `Promise`s can construct us.
    ///
    /// @param state The shared state to associate with.
    Future(const typename promise_type::_SharedStatePtr& state):
        m_state(state)
    {}

    // ------------------------------------------------------------------------------------------ //

    /// @brief Chaining for generic functors.
    ///
    /// @tparam Result  The type given functor promises.
    /// @tparam Resolve A functor type that can take a `Promise&&` as its parameter.
    ///
    /// @param resolve The functor to call when this one is resolved.
    ///
    /// @return A new future, for when the provided `resolve` completes its action.
    template<
        typename Result,
        typename Resolve,
        typename Reject,
        typename = typename std::result_of<Resolve(T&&,Promise<Result>&&)>::type
    >
    Future<Result> _then(Resolve&& resolve, Reject&& reject);

    // ------------------------------------------------------------------------------------------ //

    /// @brief Chaining for generic functors promising nothing.
    ///
    /// @tparam Resolve A functor type that can take a `Promise&&` as its parameter.
    ///
    /// @param resolve The functor to call when this one is resolved.
    ///
    /// @return A new future, for when the provided `resolve` completes its action.
    template<
        typename Resolve,
        typename Reject,
        typename = typename std::result_of<Resolve(T&&, Promise<>&&)>::type
    >
    Future<> _then(Resolve&& resolve, Reject&& reject);

    // ------------------------------------------------------------------------------------------ //

    /// @brief Chaining for `Future`-returning functors.
    ///
    /// @tparam Resolve A functor which returns a `Future`.
    ///
    /// @param resolve The functor to call when this one is ready.
    ///
    /// @return A `Future` which will be resolved by `resolve`.
    template<
        typename Resolve,
        typename Reject,
        typename ResolveResult = typename std::result_of<Resolve(T&&)>::type,
        typename std::enable_if<IsFuture<ResolveResult>::value>::type* = nullptr
    >
    Future<typename ResolveResult::result_type> _then(Resolve&& resolve, Reject&& reject);

    // ------------------------------------------------------------------------------------------ //

    /// @brief Chaining for value-returning functors (i.e. synchronous ones).
    ///
    /// @tparam Resolve A synchronous functor type.
    ///
    /// @param resolve A synchronous functor returning some value.
    ///
    /// @return A `Future` which will be resolved with the return value from `resolve`.
    template<
        typename Resolve,
        typename Reject,
        typename ResolveResult = typename std::result_of<Resolve(T&&)>::type,
        typename std::enable_if<
            !IsFuture<ResolveResult>::value &&
            !std::is_void<ResolveResult>::value
        >::type* = nullptr
    >
    Future<ResolveResult> _then(Resolve&& resolve, Reject&& reject);

    // ------------------------------------------------------------------------------------------ //

    /// @brief Chaining for void-returning synchronous functors.
    ///
    /// @tparam Resolve A synchronous functor type.
    ///
    /// @param resolve A synchronous functor with no return value.
    ///
    /// @return A `Future` which will be resolved `resolve` runs.
    template<
        typename Resolve,
        typename Reject,
        typename ResolveResult = typename std::result_of<Resolve(T&&)>::type,
        typename std::enable_if<std::is_void<ResolveResult>::value>::type* = nullptr
    >
    Future<> _then(Resolve&& resolve, Reject&& reject);

    // ------------------------------------------------------------------------------------------ //

    /// @brief Our internal shared state.
    typename promise_type::_SharedStatePtr m_state;
};

}
}
<|endoftext|>


<commit_before>#include <algorithm>

#include <QFileDialog>
#include <QMessageBox>

#include "species_dialog.h"
#include "metadata_dialog.h"
#include "annotatedregion.h"
#include "annotated_line.h"
#include "annotated_dot.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"

namespace fish_annotator { namespace image_annotator {

namespace fs = boost::filesystem;

namespace { //anonymous

static const std::vector<std::string> kDirExtensions = {
  ".jpg", ".png", ".bmp", ".tif", ".jpeg",
  ".JPG", ".PNG", ".BMP", ".TIF", ".JPEG"};

} // anonymous namespace

MainWindow::MainWindow(QWidget *parent)
  : annotations_(new ImageAnnotationList)
  , view_(new AnnotationView)
  , scene_(new AnnotationScene)
  , ui_(new Ui::MainWindow)
  , species_controls_(new SpeciesControls(this))
  , annotation_widget_(new AnnotationWidget(this))
  , global_state_widget_(new GlobalStateWidget(this))
  , image_files_()
  , metadata_()
  , species_()
  , subspecies_()
  , current_annotations_()
  , color_map_() {
  ui_->setupUi(this);
  setWindowTitle("Image Annotator");
#ifdef _WIN32
  setWindowIcon(QIcon(":/icons/cvision/cvision_no_text.ico"));
#endif
  ui_->next->setIcon(QIcon(":/icons/image_controls/next.svg"));
  ui_->prev->setIcon(QIcon(":/icons/image_controls/prev.svg"));
  ui_->videoWindowLayout->addWidget(view_.get());
  ui_->speciesLayout->addWidget(annotation_widget_.get());
  ui_->speciesLayout->addWidget(species_controls_.get());
  ui_->globalStateLayout->addWidget(global_state_widget_.get());
  tabifyDockWidget(
    ui_->globalStateDockWidget,
    ui_->navigationDockWidget);
  tabifyDockWidget(
    ui_->navigationDockWidget,
    ui_->speciesDockWidget);
  QObject::connect(species_controls_.get(), &SpeciesControls::individualAdded,
      this, &MainWindow::addIndividual);
  QObject::connect(species_controls_.get(), &SpeciesControls::colorChanged,
      this, &MainWindow::colorChanged);
  scene_->setToolWidget(annotation_widget_.get());
  QObject::connect(scene_.get(), &AnnotationScene::boxFinished,
      this, &MainWindow::addBoxAnnotation);
  QObject::connect(scene_.get(), &AnnotationScene::lineFinished,
      this, &MainWindow::addLineAnnotation);
  QObject::connect(scene_.get(), &AnnotationScene::dotFinished,
      this, &MainWindow::addDotAnnotation);
  fs::path current_path(QDir::currentPath().toStdString());
  fs::path default_species = current_path / fs::path("default.species");
  if(fs::exists(default_species)) {
    species_controls_->loadSpeciesFile(
        QString(default_species.string().c_str()));
  }
  fs::path default_colors = current_path / fs::path("default.colors");
  if(fs::exists(default_colors)) {
    species_controls_->loadColorsFile(
        QString(default_colors.string().c_str()));
  }
}

void MainWindow::resizeEvent(QResizeEvent *event) {
  view_->fitInView();
}

void MainWindow::on_next_clicked() {
  int next_val = ui_->imageSlider->value() + 1;
  if(next_val <= ui_->imageSlider->maximum()) {
    ui_->imageSlider->setValue(next_val);
  }
}

void MainWindow::on_prev_clicked() {
  int prev_val = ui_->imageSlider->value() - 1;
  if(prev_val >= ui_->imageSlider->minimum()) {
    ui_->imageSlider->setValue(prev_val);
  }
}

void MainWindow::on_loadImageDir_triggered() {
  QString image_dir = QFileDialog::getExistingDirectory(this,
    "Select an image directory.");
  if(!image_dir.isEmpty()) {
    onLoadDirectorySuccess(image_dir);
  }
}

void MainWindow::on_saveAnnotations_triggered() {
  if(image_files_.size() > 0) {
    annotations_->write(image_files_);
  }
}

void MainWindow::on_saveAnnotatedImage_triggered() {
  QString file_path = QFileDialog::getSaveFileName(
    this,
    "Select output file.",
    QCoreApplication::applicationDirPath(),
    "PNG (*.png);;JPEG (*.jpg);;BMP (*.bmp)");
  if(!file_path.isEmpty()) {
    QPixmap pixmap = view_->grab();
    pixmap.save(file_path);
  }
}

void MainWindow::on_setMetadata_triggered() {
  MetadataDialog *dlg = new MetadataDialog(this);
  dlg->setMetadata(metadata_);
  if(dlg->exec()) {
    metadata_ = dlg->getMetadata();
  }
}

void MainWindow::on_imageSlider_valueChanged() {
  scene_->clear();
  current_annotations_.clear();
  std::string filename = image_files_[ui_->imageSlider->value()].string();
  QImage current(filename.c_str());
  if(!current.isNull()) {
    scene_->addPixmap(QPixmap::fromImage(current));
    scene_->setSceneRect(current.rect());
    view_->setScene(scene_.get());
    view_->show();
    ui_->fileNameValue->setText(filename.c_str());
    fs::path img_path(filename);
    auto annotations =
      annotations_->getImageAnnotations(img_path.filename());
    for(auto annotation : annotations) {
    }
    drawAnnotations();
    updateSpeciesCounts();
    updateTypeMenus();
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", std::string(
        std::string("Error loading image ")
      + filename
      + std::string(".")).c_str());
  }
}

void MainWindow::on_showAnnotations_stateChanged() {
  on_imageSlider_valueChanged();
}

void MainWindow::on_idSelection_activated(const QString &id) {
  updateTypeMenus();
}

void MainWindow::on_typeMenu_activated(const QString &text) {
  auto ann = currentAnnotation();
  if(ann != nullptr) {
    ann->species_ = text.toStdString();
  }
  updateSpeciesCounts();
  updateTypeMenus();
}

void MainWindow::on_subTypeMenu_activated(const QString &text) {
  auto ann = currentAnnotation();
  if(ann != nullptr) {
    ann->subspecies_ = text.toStdString();
  }
}

void MainWindow::on_removeAnnotation_clicked() {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    int id = ui_->idSelection->currentText().toInt();
    annotations_->remove(current_image, id);
    on_imageSlider_valueChanged();
  }
}

void MainWindow::addIndividual(std::string species, std::string subspecies) {
  species_ = species;
  subspecies_ = subspecies;
  scene_->setMode(kSelect);
  QApplication::restoreOverrideCursor();
  scene_->setMode(kDraw);
}

void MainWindow::colorChanged(QMap<QString, QColor> color_map) {
  color_map_ = color_map;
  drawAnnotations();
}

void MainWindow::addBoxAnnotation(const QRectF &rect) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(rect.x(), rect.y(), rect.width(), rect.height()), kBox);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::addLineAnnotation(const QLineF &line) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(line.x1(), line.y1(), line.x2(), line.y2()), kLine);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::addDotAnnotation(const QPointF &dot) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(dot.x(), dot.y(), 0, 0), kDot);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::onLoadDirectorySuccess(const QString &image_dir) {
  image_files_.clear();
  annotations_->clear();
  fs::directory_iterator dir_it(image_dir.toStdString());
  fs::directory_iterator dir_end;
  fs::path current_path(QDir::currentPath().toStdString());
  fs::path default_global_state = current_path / fs::path("default.global");
  for(; dir_it != dir_end; ++dir_it) {
    fs::path ext(dir_it->path().extension());
    for(auto &ok_ext : kDirExtensions) {
      if(ext == ok_ext) {
        auto val = std::make_shared<GlobalStateAnnotation>();
        if(fs::exists(default_global_state)) {
          deserialize(*val, default_global_state.string());
        }
        annotations_->insertGlobalStateAnnotation(
          dir_it->path().filename().string(),
          val);
        image_files_.push_back(dir_it->path());
      }
    }
  }
  std::sort(image_files_.begin(), image_files_.end());
  if(image_files_.size() > 0) {
    ui_->idLabel->setEnabled(true);
    ui_->speciesLabel->setEnabled(true);
    ui_->subspeciesLabel->setEnabled(true);
    ui_->idSelection->setEnabled(true);
    ui_->typeMenu->setEnabled(true);
    ui_->subTypeMenu->setEnabled(true);
    ui_->removeAnnotation->setEnabled(true);
    ui_->showAnnotations->setEnabled(true);
    ui_->setMetadata->setEnabled(true);
    ui_->next->setEnabled(true);
    ui_->prev->setEnabled(true);
    ui_->saveAnnotations->setEnabled(true);
    ui_->saveAnnotatedImage->setEnabled(true);
    ui_->imageSlider->setEnabled(true);
    ui_->imageSlider->setMinimum(0);
    ui_->imageSlider->setMaximum(static_cast<int>(image_files_.size() - 1));
    ui_->imageSlider->setSingleStep(1);
    ui_->imageSlider->setValue(0);
    annotations_->read(image_files_);
    species_controls_->loadFromVector(annotations_->getAllSpecies());
    on_imageSlider_valueChanged();
    view_->setBoundingRect(scene_->sceneRect());
    view_->fitInView();
    scene_->setMode(kSelect);
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", "No images found in this directory.");
  }
}

std::shared_ptr<ImageAnnotation> MainWindow::currentAnnotation() {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto id = ui_->idSelection->currentText();
    auto current_image = image_files_[ui_->imageSlider->value()];
    auto annotations =
      annotations_->getImageAnnotations(current_image);
    for(auto annotation : annotations) {
      if(annotation->id_ == id.toInt()) {
        return annotation;
      }
    }
  }
  return nullptr;
}

void MainWindow::updateTypeMenus() {
  auto ann = currentAnnotation();
  ui_->typeMenu->clear();
  ui_->subTypeMenu->clear();
  if(ann != nullptr) {
    auto species = species_controls_->getSpecies();
    for(auto &s : species) {
      ui_->typeMenu->addItem(s.getName().c_str());
      if(s.getName() == ann->species_) {
        ui_->typeMenu->setCurrentText(s.getName().c_str());
        auto subspecies = s.getSubspecies();
        for(auto &sub : subspecies) {
          ui_->subTypeMenu->addItem(sub.c_str());
          if(sub == ann->subspecies_) {
            ui_->subTypeMenu->setCurrentText(sub.c_str());
          }
        }
      }
    }
  }
}

void MainWindow::drawAnnotations() {
  for(auto ann : current_annotations_) {
    scene_->removeItem(ann);
  }
  current_annotations_.clear();
  ui_->idSelection->clear();
  if(image_files_.size() > 0) {
    std::string filename = image_files_[ui_->imageSlider->value()].string();
    QImage current(filename.c_str());
    if(!current.isNull()) {
      fs::path img_path(filename);
      global_state_widget_->setStates(
        annotations_->getGlobalStateAnnotation(img_path.filename().string()));
      auto annotations =
        annotations_->getImageAnnotations(img_path.filename());
      for(auto annotation : annotations) {
        if(ui_->showAnnotations->isChecked()) {
          AnnotatedRegion<ImageAnnotation> *box = nullptr;
          AnnotatedLine<ImageAnnotation> *line = nullptr;
          AnnotatedDot<ImageAnnotation> *dot = nullptr;
          QString name = annotation->species_.c_str();
          QColor color = color_map_[name.toLower()];
          switch(annotation->type_) {
            case kBox:
              box = new AnnotatedRegion<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(box);
              current_annotations_.push_back(box);
              break;
            case kLine:
              line = new AnnotatedLine<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(line);
              current_annotations_.push_back(line);
              break;
            case kDot:
              dot = new AnnotatedDot<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(dot);
              current_annotations_.push_back(dot);
              break;
          }
        }
        ui_->idSelection->addItem(QString::number(annotation->id_));
      }
    }
  }
}

void MainWindow::updateSpeciesCounts() {
  species_controls_->resetCounts();
  std::string filename = image_files_[ui_->imageSlider->value()].string();
  fs::path img_path(filename);
  auto counts = annotations_->getCounts(img_path.filename().string());
  for(const auto &species : species_controls_->getSpecies()) {
    auto it = counts.find(species.getName());
    if(it != counts.end()) {
      species_controls_->setCount(it->second, it->first);
    }
    else {
      species_controls_->setCount(0, species.getName());
    }
  }
}

#include "moc_mainwindow.cpp"

}} // namespace fish_annotator::image_annotator
<commit_msg>Fix issues with adjusting bounding boxes<commit_after>#include <algorithm>

#include <QFileDialog>
#include <QMessageBox>

#include "species_dialog.h"
#include "metadata_dialog.h"
#include "annotatedregion.h"
#include "annotated_line.h"
#include "annotated_dot.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"

namespace fish_annotator { namespace image_annotator {

namespace fs = boost::filesystem;

namespace { //anonymous

static const std::vector<std::string> kDirExtensions = {
  ".jpg", ".png", ".bmp", ".tif", ".jpeg",
  ".JPG", ".PNG", ".BMP", ".TIF", ".JPEG"};

} // anonymous namespace

MainWindow::MainWindow(QWidget *parent)
  : annotations_(new ImageAnnotationList)
  , view_(new AnnotationView)
  , scene_(new AnnotationScene)
  , ui_(new Ui::MainWindow)
  , species_controls_(new SpeciesControls(this))
  , annotation_widget_(new AnnotationWidget(this))
  , global_state_widget_(new GlobalStateWidget(this))
  , image_files_()
  , metadata_()
  , species_()
  , subspecies_()
  , current_annotations_()
  , color_map_() {
  ui_->setupUi(this);
  setWindowTitle("Image Annotator");
#ifdef _WIN32
  setWindowIcon(QIcon(":/icons/cvision/cvision_no_text.ico"));
#endif
  ui_->next->setIcon(QIcon(":/icons/image_controls/next.svg"));
  ui_->prev->setIcon(QIcon(":/icons/image_controls/prev.svg"));
  ui_->videoWindowLayout->addWidget(view_.get());
  ui_->speciesLayout->addWidget(annotation_widget_.get());
  ui_->speciesLayout->addWidget(species_controls_.get());
  ui_->globalStateLayout->addWidget(global_state_widget_.get());
  tabifyDockWidget(
    ui_->globalStateDockWidget,
    ui_->navigationDockWidget);
  tabifyDockWidget(
    ui_->navigationDockWidget,
    ui_->speciesDockWidget);
  QObject::connect(species_controls_.get(), &SpeciesControls::individualAdded,
      this, &MainWindow::addIndividual);
  QObject::connect(species_controls_.get(), &SpeciesControls::colorChanged,
      this, &MainWindow::colorChanged);
  scene_->setToolWidget(annotation_widget_.get());
  QObject::connect(scene_.get(), &AnnotationScene::boxFinished,
      this, &MainWindow::addBoxAnnotation);
  QObject::connect(scene_.get(), &AnnotationScene::lineFinished,
      this, &MainWindow::addLineAnnotation);
  QObject::connect(scene_.get(), &AnnotationScene::dotFinished,
      this, &MainWindow::addDotAnnotation);
  fs::path current_path(QDir::currentPath().toStdString());
  fs::path default_species = current_path / fs::path("default.species");
  if(fs::exists(default_species)) {
    species_controls_->loadSpeciesFile(
        QString(default_species.string().c_str()));
  }
  fs::path default_colors = current_path / fs::path("default.colors");
  if(fs::exists(default_colors)) {
    species_controls_->loadColorsFile(
        QString(default_colors.string().c_str()));
  }
}

void MainWindow::resizeEvent(QResizeEvent *event) {
  view_->fitInView();
}

void MainWindow::on_next_clicked() {
  int next_val = ui_->imageSlider->value() + 1;
  if(next_val <= ui_->imageSlider->maximum()) {
    ui_->imageSlider->setValue(next_val);
  }
}

void MainWindow::on_prev_clicked() {
  int prev_val = ui_->imageSlider->value() - 1;
  if(prev_val >= ui_->imageSlider->minimum()) {
    ui_->imageSlider->setValue(prev_val);
  }
}

void MainWindow::on_loadImageDir_triggered() {
  QString image_dir = QFileDialog::getExistingDirectory(this,
    "Select an image directory.");
  if(!image_dir.isEmpty()) {
    onLoadDirectorySuccess(image_dir);
  }
}

void MainWindow::on_saveAnnotations_triggered() {
  if(image_files_.size() > 0) {
    annotations_->write(image_files_);
  }
}

void MainWindow::on_saveAnnotatedImage_triggered() {
  QString file_path = QFileDialog::getSaveFileName(
    this,
    "Select output file.",
    QCoreApplication::applicationDirPath(),
    "PNG (*.png);;JPEG (*.jpg);;BMP (*.bmp)");
  if(!file_path.isEmpty()) {
    QPixmap pixmap = view_->grab();
    pixmap.save(file_path);
  }
}

void MainWindow::on_setMetadata_triggered() {
  MetadataDialog *dlg = new MetadataDialog(this);
  dlg->setMetadata(metadata_);
  if(dlg->exec()) {
    metadata_ = dlg->getMetadata();
  }
}

void MainWindow::on_imageSlider_valueChanged() {
  scene_->clear();
  current_annotations_.clear();
  std::string filename = image_files_[ui_->imageSlider->value()].string();
  QImage current(filename.c_str());
  if(!current.isNull()) {
    scene_->addPixmap(QPixmap::fromImage(current));
    scene_->setSceneRect(current.rect());
    view_->setScene(scene_.get());
    view_->show();
    ui_->fileNameValue->setText(filename.c_str());
    fs::path img_path(filename);
    auto annotations =
      annotations_->getImageAnnotations(img_path.filename());
    for(auto annotation : annotations) {
    }
    drawAnnotations();
    updateSpeciesCounts();
    updateTypeMenus();
    scene_->setMode(kSelect);
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", std::string(
        std::string("Error loading image ")
      + filename
      + std::string(".")).c_str());
  }
}

void MainWindow::on_showAnnotations_stateChanged() {
  on_imageSlider_valueChanged();
}

void MainWindow::on_idSelection_activated(const QString &id) {
  updateTypeMenus();
}

void MainWindow::on_typeMenu_activated(const QString &text) {
  auto ann = currentAnnotation();
  if(ann != nullptr) {
    ann->species_ = text.toStdString();
  }
  updateSpeciesCounts();
  updateTypeMenus();
}

void MainWindow::on_subTypeMenu_activated(const QString &text) {
  auto ann = currentAnnotation();
  if(ann != nullptr) {
    ann->subspecies_ = text.toStdString();
  }
}

void MainWindow::on_removeAnnotation_clicked() {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    int id = ui_->idSelection->currentText().toInt();
    annotations_->remove(current_image, id);
    on_imageSlider_valueChanged();
  }
}

void MainWindow::addIndividual(std::string species, std::string subspecies) {
  species_ = species;
  subspecies_ = subspecies;
  scene_->setMode(kSelect);
  QApplication::restoreOverrideCursor();
  scene_->setMode(kDraw);
}

void MainWindow::colorChanged(QMap<QString, QColor> color_map) {
  color_map_ = color_map;
  drawAnnotations();
}

void MainWindow::addBoxAnnotation(const QRectF &rect) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(rect.x(), rect.y(), rect.width(), rect.height()), kBox);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::addLineAnnotation(const QLineF &line) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(line.x1(), line.y1(), line.x2(), line.y2()), kLine);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::addDotAnnotation(const QPointF &dot) {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto current_image = image_files_[ui_->imageSlider->value()];
    uint64_t id = annotations_->nextId(current_image);
    auto annotation = std::make_shared<ImageAnnotation>(
      current_image.filename().string(), species_, subspecies_, id,
      Rect(dot.x(), dot.y(), 0, 0), kDot);
    annotations_->insert(annotation);
    ui_->idSelection->setCurrentText(QString::number(id));
    drawAnnotations();
    updateTypeMenus();
    updateSpeciesCounts();
  }
}

void MainWindow::onLoadDirectorySuccess(const QString &image_dir) {
  image_files_.clear();
  annotations_->clear();
  fs::directory_iterator dir_it(image_dir.toStdString());
  fs::directory_iterator dir_end;
  fs::path current_path(QDir::currentPath().toStdString());
  fs::path default_global_state = current_path / fs::path("default.global");
  for(; dir_it != dir_end; ++dir_it) {
    fs::path ext(dir_it->path().extension());
    for(auto &ok_ext : kDirExtensions) {
      if(ext == ok_ext) {
        auto val = std::make_shared<GlobalStateAnnotation>();
        if(fs::exists(default_global_state)) {
          deserialize(*val, default_global_state.string());
        }
        annotations_->insertGlobalStateAnnotation(
          dir_it->path().filename().string(),
          val);
        image_files_.push_back(dir_it->path());
      }
    }
  }
  std::sort(image_files_.begin(), image_files_.end());
  if(image_files_.size() > 0) {
    ui_->idLabel->setEnabled(true);
    ui_->speciesLabel->setEnabled(true);
    ui_->subspeciesLabel->setEnabled(true);
    ui_->idSelection->setEnabled(true);
    ui_->typeMenu->setEnabled(true);
    ui_->subTypeMenu->setEnabled(true);
    ui_->removeAnnotation->setEnabled(true);
    ui_->showAnnotations->setEnabled(true);
    ui_->setMetadata->setEnabled(true);
    ui_->next->setEnabled(true);
    ui_->prev->setEnabled(true);
    ui_->saveAnnotations->setEnabled(true);
    ui_->saveAnnotatedImage->setEnabled(true);
    ui_->imageSlider->setEnabled(true);
    ui_->imageSlider->setMinimum(0);
    ui_->imageSlider->setMaximum(static_cast<int>(image_files_.size() - 1));
    ui_->imageSlider->setSingleStep(1);
    ui_->imageSlider->setValue(0);
    annotations_->read(image_files_);
    species_controls_->loadFromVector(annotations_->getAllSpecies());
    on_imageSlider_valueChanged();
    view_->setBoundingRect(scene_->sceneRect());
    view_->fitInView();
    scene_->setMode(kSelect);
  }
  else {
    QMessageBox err;
    err.critical(0, "Error", "No images found in this directory.");
  }
}

std::shared_ptr<ImageAnnotation> MainWindow::currentAnnotation() {
  if(image_files_.size() > 0 && ui_->imageSlider->isEnabled()) {
    auto id = ui_->idSelection->currentText();
    auto current_image = image_files_[ui_->imageSlider->value()];
    auto annotations =
      annotations_->getImageAnnotations(current_image);
    for(auto annotation : annotations) {
      if(annotation->id_ == id.toInt()) {
        return annotation;
      }
    }
  }
  return nullptr;
}

void MainWindow::updateTypeMenus() {
  auto ann = currentAnnotation();
  ui_->typeMenu->clear();
  ui_->subTypeMenu->clear();
  if(ann != nullptr) {
    auto species = species_controls_->getSpecies();
    for(auto &s : species) {
      ui_->typeMenu->addItem(s.getName().c_str());
      if(s.getName() == ann->species_) {
        ui_->typeMenu->setCurrentText(s.getName().c_str());
        auto subspecies = s.getSubspecies();
        for(auto &sub : subspecies) {
          ui_->subTypeMenu->addItem(sub.c_str());
          if(sub == ann->subspecies_) {
            ui_->subTypeMenu->setCurrentText(sub.c_str());
          }
        }
      }
    }
  }
}

void MainWindow::drawAnnotations() {
  for(auto ann : current_annotations_) {
    scene_->removeItem(ann);
  }
  current_annotations_.clear();
  ui_->idSelection->clear();
  if(image_files_.size() > 0) {
    std::string filename = image_files_[ui_->imageSlider->value()].string();
    QImage current(filename.c_str());
    if(!current.isNull()) {
      fs::path img_path(filename);
      global_state_widget_->setStates(
        annotations_->getGlobalStateAnnotation(img_path.filename().string()));
      auto annotations =
        annotations_->getImageAnnotations(img_path.filename());
      for(auto annotation : annotations) {
        if(ui_->showAnnotations->isChecked()) {
          AnnotatedRegion<ImageAnnotation> *box = nullptr;
          AnnotatedLine<ImageAnnotation> *line = nullptr;
          AnnotatedDot<ImageAnnotation> *dot = nullptr;
          QString name = annotation->species_.c_str();
          QColor color = color_map_[name.toLower()];
          switch(annotation->type_) {
            case kBox:
              box = new AnnotatedRegion<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(box);
              current_annotations_.push_back(box);
              break;
            case kLine:
              line = new AnnotatedLine<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(line);
              current_annotations_.push_back(line);
              break;
            case kDot:
              dot = new AnnotatedDot<ImageAnnotation>(
                  annotation->id_, annotation, current.rect(), color);
              scene_->addItem(dot);
              current_annotations_.push_back(dot);
              break;
          }
        }
        ui_->idSelection->addItem(QString::number(annotation->id_));
      }
    }
  }
}

void MainWindow::updateSpeciesCounts() {
  species_controls_->resetCounts();
  std::string filename = image_files_[ui_->imageSlider->value()].string();
  fs::path img_path(filename);
  auto counts = annotations_->getCounts(img_path.filename().string());
  for(const auto &species : species_controls_->getSpecies()) {
    auto it = counts.find(species.getName());
    if(it != counts.end()) {
      species_controls_->setCount(it->second, it->first);
    }
    else {
      species_controls_->setCount(0, species.getName());
    }
  }
}

#include "moc_mainwindow.cpp"

}} // namespace fish_annotator::image_annotator
<|endoftext|>


<commit_before>#include <Salamandre-stats/stats.hpp>
#include <Salamandre-daemon/Daemon.hpp>
#include <Salamandre-daemon/ServerBroadcast.hpp>
#include <Salamandre-daemon/define.hpp>

#include <utils/log.hpp>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <libgen.h>
#include <fcntl.h>
#include <unistd.h>

#include <iostream>
#include <csignal>
#include <tclap/CmdLine.h>

namespace salamandre
{
    Daemon* daemon = nullptr;
    int fd_pid;
}

void stop_server_handler(int sig)
{
    utils::log::info("Stop","Recv signal",sig,"Stoping server.\n Please wait.");
    if(salamandre::daemon)
        salamandre::daemon->stop();
    close(salamandre::fd_pid);
}

void daemonize(std::string log_path, std::string pid_path)
{

    int pid = ::fork();

    if (pid < 0)
    {
        std::cerr << "Unable to fork, exiting" << std::endl;
        ::exit(1);
    } else if( pid != 0)
    {
        // Parent process, exiting
        std::cout << "Turning into background... PID: " << pid << std::endl;
        ::exit(0);
    }

    // Detaching from any controlling terminal
    if (::setsid() == -1)
    {
        ::perror("Unable to detach from terminal");
        ::exit(1);
    }

    salamandre::fd_pid = ::open(pid_path.c_str(), O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (salamandre::fd_pid == -1)
    {
        ::perror("Unable to create and open the pid_file");
        ::close(salamandre::fd_pid);
        ::exit(1);
    }

    if (::flock(salamandre::fd_pid, LOCK_EX | LOCK_NB) != 0)
    {
        ::perror("Unable to lock the pid_file, is the daemon already running ?");
        ::close(salamandre::fd_pid);
        ::exit(1);
    }
    ::ftruncate(salamandre::fd_pid, 0);

    char buffer[32];
    ::snprintf(buffer, 32, "%d", ::getpid());
    ::write(salamandre::fd_pid, buffer, strlen(buffer));
    ::syncfs(salamandre::fd_pid);

   std::ifstream in("/dev/null");
   std::cin.rdbuf(in.rdbuf());

   // That should be the good solution, but it causes segfaults
   /*
   std::ofstream out(log_path.c_str());
   std::cout.rdbuf(out.rdbuf());
   std::cerr.rdbuf(out.rdbuf());
   */

   FILE * log_file = ::fopen(log_path.c_str(), "a");
   ::close(STDOUT_FILENO);
   ::dup(::fileno(log_file));
   ::dup2(STDOUT_FILENO, STDERR_FILENO);
   ::fclose(log_file);
}

int main(int argc,char* argv[])
{
    int gui_port = 3842;
    int server_port = 3843;
    int broadcast_port = 5001;
    std::string node_file_path;
    bool local_only = false;
    bool daemon = false;
    std::string path = argv[0];
    try {

        TCLAP::CmdLine cmd("Salamandre Daemon", ' ', DAEMON_VERSION);
        TCLAP::ValueArg<int> gui_port_arg("g", "gui-port", "Port for the GUI", false, gui_port, "int", cmd);
        TCLAP::ValueArg<int> server_port_arg("s", "server-port", "Port for the server to listen", false, server_port, "int", cmd);
        TCLAP::ValueArg<std::string> nodes_file_arg("f", "nodes-file", "File containing node list", false, "", "string", cmd);
        TCLAP::SwitchArg local_switch("l", "local", "Whether daemon sould run stricly locally", cmd, false);
        TCLAP::SwitchArg daemon_switch("d", "daemonize", "Start the server in a daemon", cmd, false);

        cmd.parse(argc, argv);

        local_only = local_switch.getValue();
        gui_port = gui_port_arg.getValue();
        server_port = server_port_arg.getValue();
        node_file_path = nodes_file_arg.getValue();
        daemon = daemon_switch.getValue();

    } catch(TCLAP::ArgException &e) {
        utils::log::critical(1,"Args ","error",e.error(),"for arg",e.argId());
    }
    std::cout<<"Daemon start on:"
        <<"\n\tgui listen on port : "<<gui_port
        <<"\n\tfile server listen on port : "<<server_port
        <<"\n\tbrodcast server listen on port: "<<broadcast_port
        <<"\n\tlocal only : "<<local_only
        <<"\nPress ^C to exit"
        <<std::endl<<std::endl;
    std::signal(SIGINT, stop_server_handler);
    std::signal(SIGTERM, stop_server_handler);

    std::string log_file = "salamandre.log";
    std::string pid_file = "salamandre.pid";
    if (daemon)
    {
        daemonize(log_file, pid_file);
    }
    try
    {
        salamandre::Daemon::init();

        if (!node_file_path.empty())
        {
            salamandre::stats::Stats::load(node_file_path.c_str());
        }

        salamandre::daemon = new salamandre::Daemon(path, gui_port,server_port,broadcast_port,local_only);
        salamandre::daemon->start();

        salamandre::daemon->wait();

        delete salamandre::daemon;
        salamandre::daemon = nullptr;

        salamandre::Daemon::close();

    }
    catch(ntw::SocketExeption& e)
    {
        utils::log::error("Socket error",e.what());
    }

    utils::log::info("End","Daemon is now close, Good bye");

    if (daemon)
    {
        ::unlink(pid_file.c_str());
    }
    return 0;
}
<commit_msg>[daemon] Add header for the log file, remove unecessary includes<commit_after>#include <Salamandre-stats/stats.hpp>
#include <Salamandre-daemon/Daemon.hpp>
#include <Salamandre-daemon/ServerBroadcast.hpp>
#include <Salamandre-daemon/define.hpp>

#include <utils/log.hpp>

#include <sys/file.h>
#include <unistd.h>

#include <iostream>
#include <csignal>
#include <tclap/CmdLine.h>

namespace salamandre
{
    Daemon* daemon = nullptr;
    int fd_pid;
}

void stop_server_handler(int sig)
{
    utils::log::info("Stop","Recv signal",sig,"Stoping server.\n Please wait.");
    if(salamandre::daemon)
        salamandre::daemon->stop();
    close(salamandre::fd_pid);
}

void daemonize(std::string log_path, std::string pid_path)
{

    int pid = ::fork();

    if (pid < 0)
    {
        std::cerr << "Unable to fork, exiting" << std::endl;
        ::exit(1);
    } else if( pid != 0)
    {
        // Parent process, exiting
        std::cout << "Turning into background... PID: " << pid << std::endl;
        ::exit(0);
    }

    // Detaching from any controlling terminal
    if (::setsid() == -1)
    {
        ::perror("Unable to detach from terminal");
        ::exit(1);
    }

    salamandre::fd_pid = ::open(pid_path.c_str(), O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (salamandre::fd_pid == -1)
    {
        ::perror("Unable to create and open the pid_file");
        ::close(salamandre::fd_pid);
        ::exit(1);
    }

    if (::flock(salamandre::fd_pid, LOCK_EX | LOCK_NB) != 0)
    {
        ::perror("Unable to lock the pid_file, is the daemon already running ?");
        ::close(salamandre::fd_pid);
        ::exit(1);
    }
    ::ftruncate(salamandre::fd_pid, 0);

    char buffer[32];
    ::snprintf(buffer, 32, "%d", ::getpid());
    ::write(salamandre::fd_pid, buffer, strlen(buffer));
    ::syncfs(salamandre::fd_pid);

   std::ifstream in("/dev/null");
   std::cin.rdbuf(in.rdbuf());

   // That should be the good solution, but it causes segfaults
   /*
   std::ofstream out(log_path.c_str());
   std::cout.rdbuf(out.rdbuf());
   std::cerr.rdbuf(out.rdbuf());
   */

   FILE * log_file = ::fopen(log_path.c_str(), "a");
   ::close(STDOUT_FILENO);
   ::dup(::fileno(log_file));
   ::dup2(STDOUT_FILENO, STDERR_FILENO);
   ::fclose(log_file);
}

int main(int argc,char* argv[])
{
    int gui_port = 3842;
    int server_port = 3843;
    int broadcast_port = 5001;
    std::string node_file_path;
    bool local_only = false;
    bool daemon = false;
    std::string path = argv[0];
    try {

        TCLAP::CmdLine cmd("Salamandre Daemon", ' ', DAEMON_VERSION);
        TCLAP::ValueArg<int> gui_port_arg("g", "gui-port", "Port for the GUI", false, gui_port, "int", cmd);
        TCLAP::ValueArg<int> server_port_arg("s", "server-port", "Port for the server to listen", false, server_port, "int", cmd);
        TCLAP::ValueArg<std::string> nodes_file_arg("f", "nodes-file", "File containing node list", false, "", "string", cmd);
        TCLAP::SwitchArg local_switch("l", "local", "Whether daemon sould run stricly locally", cmd, false);
        TCLAP::SwitchArg daemon_switch("d", "daemonize", "Start the server in a daemon", cmd, false);

        cmd.parse(argc, argv);

        local_only = local_switch.getValue();
        gui_port = gui_port_arg.getValue();
        server_port = server_port_arg.getValue();
        node_file_path = nodes_file_arg.getValue();
        daemon = daemon_switch.getValue();

    } catch(TCLAP::ArgException &e) {
        utils::log::critical(1,"Args ","error",e.error(),"for arg",e.argId());
    }
    std::cout<<"Daemon start on:"
        <<"\n\tgui listen on port : "<<gui_port
        <<"\n\tfile server listen on port : "<<server_port
        <<"\n\tbrodcast server listen on port: "<<broadcast_port
        <<"\n\tlocal only : "<<local_only
        <<"\nPress ^C to exit"
        <<std::endl<<std::endl;
    std::signal(SIGINT, stop_server_handler);
    std::signal(SIGTERM, stop_server_handler);

    std::string log_file = "salamandre.log";
    std::string pid_file = "salamandre.pid";
    if (daemon)
    {
        daemonize(log_file, pid_file);
        std::cout << "Daemon started on gui port : " << gui_port << ", server port : " << server_port << ", broadcast port : " << broadcast_port << std::endl;
    }
    try
    {
        salamandre::Daemon::init();

        if (!node_file_path.empty())
        {
            salamandre::stats::Stats::load(node_file_path.c_str());
        }

        salamandre::daemon = new salamandre::Daemon(path, gui_port,server_port,broadcast_port,local_only);
        salamandre::daemon->start();

        salamandre::daemon->wait();

        delete salamandre::daemon;
        salamandre::daemon = nullptr;

        salamandre::Daemon::close();

    }
    catch(ntw::SocketExeption& e)
    {
        utils::log::error("Socket error",e.what());
    }

    utils::log::info("End","Daemon is now close, Good bye");

    if (daemon)
    {
        ::unlink(pid_file.c_str());
    }
    return 0;
}
<|endoftext|>


<commit_before>#include <mlopen.h>
#include "test.hpp"
#include <array>
#include <iterator>
#include <memory>
#include <utility>
#include <iostream>
#include <mlopen/tensor.hpp>
#include <mlopen/convolution.hpp>
#include <limits>

// #include "network_data.hpp"
#include "tensor_holder.hpp"
#include "verify.hpp"
#include "driver.hpp"
#include "get_handle.hpp"

template<class T>
tensor<T> get_output_tensor(const mlopen::ConvolutionDescriptor& filter, const tensor<T>& input, const tensor<T>& weights)
{
    assert(filter.GetBackwardOutputTensor(filter.GetForwardOutputTensor(input.desc, weights.desc), weights.desc) == input.desc);
    return tensor<T>{filter.GetForwardOutputTensor(input.desc, weights.desc)};
}

struct verify_forward_conv
{
    template<class T>
    tensor<T> cpu(const tensor<T>& input, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int bias = 0)
    {
        auto out = get_output_tensor(filter, input, weights);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = mlopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = mlopen::tie4(weights.desc.GetLengths());

        out.par_for_each([&](int o, int w, int i, int j)
        {
            const int start_x = i * filter.v - filter.pad_h;
            const int start_y = j * filter.u - filter.pad_w;

            double acc = bias;
            ford(wei_c, wei_h, wei_w)([&](int k, int x, int y)
            {
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    acc += input(o, k, in_x, in_y) * weights(w, k, x, y);
                }
            });
            out(o, w, i, j) = acc;
        });
        return out;
    }

    template<class T>
    tensor<T> gpu(const tensor<T>& input, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int /* bias */ = 0)
    {
        auto&& handle = get_handle();
        auto out = get_output_tensor(filter, input, weights);

        auto in_dev = handle.Write(input.data);
        auto wei_dev = handle.Write(weights.data);
        auto out_dev = handle.Create<T>(out.data.size());

		size_t workspace_size = filter.ForwardGetWorkSpaceSize(weights.desc, out.desc);

		std::vector<char> workspace(workspace_size);
		auto workspace_dev = handle.Write(workspace);

        int ret_algo_count;
        mlopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;

        filter.FindConvFwdAlgorithm(handle,
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            out.desc,
            out_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            mlopenConvolutionFastest,
            workspace_dev.get(),
            workspace_size,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionForward(handle,
            &alpha,
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            perf.fwd_algo,
            &beta,
            out.desc,
            out_dev.get(),
            workspace_dev.get(),
            workspace_size);

        out.data = handle.Read<T>(out_dev, out.data.size());
        return out;
    }

    template<class T>
    void fail(float, const tensor<T>& input, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int /*bias*/ = 0)
    {
        std::cout << "Forward convolution: " << std::endl;
        std::cout << "Input tensor: " << input.desc.ToString() << std::endl;
        std::cout << "Output tensor: " << filter.GetForwardOutputTensor(input.desc, weights.desc).ToString() << std::endl;
        std::cout << "Weights tensor: " << weights.desc.ToString() << std::endl;
    }

};

template<class T>
tensor<T> get_input_tensor(const mlopen::ConvolutionDescriptor& filter, const tensor<T>& out, const tensor<T>& weights)
{
    assert(filter.GetForwardOutputTensor(filter.GetBackwardOutputTensor(out.desc, weights.desc), weights.desc) == out.desc);
    return tensor<T>{filter.GetBackwardOutputTensor(out.desc, weights.desc)};
}

struct verify_backward_conv
{
    template<class T>
    tensor<T> cpu(const tensor<T>& out, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int /* bias */ = 0)
    {
        auto input = get_input_tensor(filter, out, weights);
        std::fill(input.begin(), input.end(), 0);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = mlopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = mlopen::tie4(weights.desc.GetLengths());

        int out_n, out_c, out_h, out_w;
        std::tie(out_n, out_c, out_h, out_w) = mlopen::tie4(out.desc.GetLengths());

        par_ford(out_n, wei_c)([&](int o, int k)
        {
            ford(out_c, out_h, out_w, wei_h, wei_w)([&](int w, int i, int j, int x, int y)
            {
                const int start_x = i * filter.v - filter.pad_h;
                const int start_y = j * filter.u - filter.pad_w;
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    input(o, k, in_x, in_y) += out(o, w, i, j) * weights(w, k, x, y);
                }
            });
        });
        return input;
    }

    template<class T>
    tensor<T> gpu(const tensor<T>& out, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int /* bias */ = 0)
    {
        auto&& handle = get_handle();
        auto input = get_input_tensor(filter, out, weights);
        std::fill(input.begin(), input.end(), 0);

        auto out_dev = handle.Write(out.data);
        auto wei_dev = handle.Write(weights.data);
        auto in_dev = handle.Create<T>(input.data.size());

        int ret_algo_count;
        mlopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;

        filter.FindConvBwdDataAlgorithm(handle,
            out.desc,
            out_dev.get(),
            weights.desc,
            wei_dev.get(),
            input.desc,
            in_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            mlopenConvolutionFastest,
            nullptr,
            10,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionBackwardData(handle,
            &alpha,
            out.desc,
            out_dev.get(),
            weights.desc,
            wei_dev.get(),
            perf.bwd_data_algo,
            &beta,
            input.desc,
            in_dev.get(),
            nullptr,
            0);

        input.data = handle.Read<T>(in_dev, input.data.size());
        return input;
    }

    template<class T>
    void fail(float, const tensor<T>& output, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int /*bias*/ = 0)
    {
        std::cout << "Backward convolution: " << std::endl;
        std::cout << "Input tensor: " << filter.GetBackwardOutputTensor(output.desc, weights.desc).ToString() << std::endl;
        std::cout << "Output tensor: " << output.desc.ToString() << std::endl;
        std::cout << "Weights tensor: " << weights.desc.ToString() << std::endl;
    }

};

template<class T>
tensor<T> get_weight_tensor(const mlopen::ConvolutionDescriptor& filter, const tensor<T>& input, const tensor<T>& out)
{
    return tensor<T>{filter.GetBackwardWeightsTensor(input.desc, out.desc)};
}

struct verify_backward_weights_conv
{
    template<class T>
    tensor<T> cpu(const tensor<T>& input, const tensor<T>& out, const mlopen::ConvolutionDescriptor& filter, int /* bias */ = 0)
    {
        auto weights = get_weight_tensor(filter, input, out);
        std::fill(weights.begin(), weights.end(), 0);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = mlopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = mlopen::tie4(weights.desc.GetLengths());

        int out_n, out_c, out_h, out_w;
        std::tie(out_n, out_c, out_h, out_w) = mlopen::tie4(out.desc.GetLengths());

        par_ford(out_c, wei_c, wei_h, wei_w)([&](int w, int k, int x, int y)
        {
            double acc = 0.0;
            ford(out_n, out_h, out_w)([&](int o, int i, int j)
            {
                const int start_x = i * filter.v - filter.pad_h;
                const int start_y = j * filter.u - filter.pad_w;
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    acc += input(o, k, in_x, in_y) * out(o, w, i, j);
                }
            });
            weights(w, k, x, y) = acc;
        });
        return weights;
    }

    template<class T>
    tensor<T> gpu(const tensor<T>& input, const tensor<T>& out, const mlopen::ConvolutionDescriptor& filter, int /* bias */ = 0)
    {
        auto&& handle = get_handle();
        auto weights = get_weight_tensor(filter, input, out);
        std::fill(weights.begin(), weights.end(), 0);

        auto out_dev = handle.Write(out.data);
        auto wei_dev = handle.Create<T>(weights.data.size());
        auto in_dev = handle.Write(input.data);

        std::size_t workspace_size = filter.ConvolutionBackwardWeightsGetWorkSpaceSize(out.desc, input.desc, weights.desc);

        std::vector<char> workspace(workspace_size);
        auto workspace_dev = handle.Write(workspace);

#if 0
#if MLOPEN_USE_TINYGEMM
        auto algo = mlopenConvolutionBwdWeightsAlgoGEMM;
#else
        auto algo = mlopenConvolutionBwdWeightsAlgoDirect;
#endif
#endif
        int ret_algo_count;
        mlopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;
        filter.FindConvBwdWeightsAlgorithm(handle,
            out.desc,
            out_dev.get(),
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            mlopenConvolutionFastest,
            workspace_dev.get(),
            workspace_size,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionBackwardWeights(handle,
            &alpha,
            out.desc,
            out_dev.get(),
            input.desc,
            in_dev.get(),
            perf.bwd_weights_algo,
            &beta,
            weights.desc,
            wei_dev.get(),
            workspace_dev.get(),
            workspace_size);

        weights.data = handle.Read<T>(wei_dev, weights.data.size());
        return weights;
    }

    template<class T>
    void fail(float, const tensor<T>& input, const tensor<T>& output, const mlopen::ConvolutionDescriptor& filter, int /*bias*/ = 0)
    {
        std::cout << "Backward weights convolution: " << std::endl;
        std::cout << "Input tensor: " << input.desc.ToString() << std::endl;
        std::cout << "Output tensor: " << output.desc.ToString() << std::endl;
        std::cout << "Weights tensor: " << filter.GetBackwardWeightsTensor(input.desc, output.desc).ToString() << std::endl;
    }

};

template<class T>
struct conv_driver : test_driver
{
    tensor<T> input;
    tensor<T> weights;
    mlopen::ConvolutionDescriptor filter;

    conv_driver()
    {
        add(input, "input", get_input_tensor());
        add(weights, "weights", get_weights_tensor());
        add(filter, "filter", generate_single(mlopen::ConvolutionDescriptor{0, 0}));
    }

    void run()
    {
        if (input.desc.GetLengths().at(1) == weights.desc.GetLengths().at(1))
        {
            auto out_p = verify(verify_forward_conv{}, input, weights, filter);
            for(auto& x:out_p.first) x = (long(x+1)*2) % 17; // Clamp big numbers
            verify(verify_backward_conv{}, out_p.first, weights, filter);
#if MLOPEN_USE_TINYGEMM
            verify(verify_backward_weights_conv{}, input, out_p.first, filter);
#endif
        }
    }
};

int main(int argc, const char *argv[])
{
    test_drive<conv_driver<float>>(argc, argv);
}
<commit_msg>zero check for workspace<commit_after>#include <mlopen.h>
#include "test.hpp"
#include <array>
#include <iterator>
#include <memory>
#include <utility>
#include <iostream>
#include <mlopen/tensor.hpp>
#include <mlopen/convolution.hpp>
#include <limits>

// #include "network_data.hpp"
#include "tensor_holder.hpp"
#include "verify.hpp"
#include "driver.hpp"
#include "get_handle.hpp"

template<class T>
tensor<T> get_output_tensor(const mlopen::ConvolutionDescriptor& filter, const tensor<T>& input, const tensor<T>& weights)
{
    assert(filter.GetBackwardOutputTensor(filter.GetForwardOutputTensor(input.desc, weights.desc), weights.desc) == input.desc);
    return tensor<T>{filter.GetForwardOutputTensor(input.desc, weights.desc)};
}

struct verify_forward_conv
{
    template<class T>
    tensor<T> cpu(const tensor<T>& input, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int bias = 0)
    {
        auto out = get_output_tensor(filter, input, weights);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = mlopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = mlopen::tie4(weights.desc.GetLengths());

        out.par_for_each([&](int o, int w, int i, int j)
        {
            const int start_x = i * filter.v - filter.pad_h;
            const int start_y = j * filter.u - filter.pad_w;

            double acc = bias;
            ford(wei_c, wei_h, wei_w)([&](int k, int x, int y)
            {
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    acc += input(o, k, in_x, in_y) * weights(w, k, x, y);
                }
            });
            out(o, w, i, j) = acc;
        });
        return out;
    }

    template<class T>
    tensor<T> gpu(const tensor<T>& input, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int /* bias */ = 0)
    {
        auto&& handle = get_handle();
        auto out = get_output_tensor(filter, input, weights);

        auto in_dev = handle.Write(input.data);
        auto wei_dev = handle.Write(weights.data);
        auto out_dev = handle.Create<T>(out.data.size());

		size_t workspace_size = filter.ForwardGetWorkSpaceSize(weights.desc, out.desc);

		std::vector<char> workspace(workspace_size);
		auto workspace_dev = workspace_size != 0 ? handle.Write(workspace) : nullptr;

        int ret_algo_count;
        mlopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;

        filter.FindConvFwdAlgorithm(handle,
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            out.desc,
            out_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            mlopenConvolutionFastest,
            workspace_dev.get(),
            workspace_size,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionForward(handle,
            &alpha,
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            perf.fwd_algo,
            &beta,
            out.desc,
            out_dev.get(),
            workspace_dev.get(),
            workspace_size);

        out.data = handle.Read<T>(out_dev, out.data.size());
        return out;
    }

    template<class T>
    void fail(float, const tensor<T>& input, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int /*bias*/ = 0)
    {
        std::cout << "Forward convolution: " << std::endl;
        std::cout << "Input tensor: " << input.desc.ToString() << std::endl;
        std::cout << "Output tensor: " << filter.GetForwardOutputTensor(input.desc, weights.desc).ToString() << std::endl;
        std::cout << "Weights tensor: " << weights.desc.ToString() << std::endl;
    }

};

template<class T>
tensor<T> get_input_tensor(const mlopen::ConvolutionDescriptor& filter, const tensor<T>& out, const tensor<T>& weights)
{
    assert(filter.GetForwardOutputTensor(filter.GetBackwardOutputTensor(out.desc, weights.desc), weights.desc) == out.desc);
    return tensor<T>{filter.GetBackwardOutputTensor(out.desc, weights.desc)};
}

struct verify_backward_conv
{
    template<class T>
    tensor<T> cpu(const tensor<T>& out, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int /* bias */ = 0)
    {
        auto input = get_input_tensor(filter, out, weights);
        std::fill(input.begin(), input.end(), 0);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = mlopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = mlopen::tie4(weights.desc.GetLengths());

        int out_n, out_c, out_h, out_w;
        std::tie(out_n, out_c, out_h, out_w) = mlopen::tie4(out.desc.GetLengths());

        par_ford(out_n, wei_c)([&](int o, int k)
        {
            ford(out_c, out_h, out_w, wei_h, wei_w)([&](int w, int i, int j, int x, int y)
            {
                const int start_x = i * filter.v - filter.pad_h;
                const int start_y = j * filter.u - filter.pad_w;
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    input(o, k, in_x, in_y) += out(o, w, i, j) * weights(w, k, x, y);
                }
            });
        });
        return input;
    }

    template<class T>
    tensor<T> gpu(const tensor<T>& out, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int /* bias */ = 0)
    {
        auto&& handle = get_handle();
        auto input = get_input_tensor(filter, out, weights);
        std::fill(input.begin(), input.end(), 0);

        auto out_dev = handle.Write(out.data);
        auto wei_dev = handle.Write(weights.data);
        auto in_dev = handle.Create<T>(input.data.size());

        int ret_algo_count;
        mlopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;

        filter.FindConvBwdDataAlgorithm(handle,
            out.desc,
            out_dev.get(),
            weights.desc,
            wei_dev.get(),
            input.desc,
            in_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            mlopenConvolutionFastest,
            nullptr,
            10,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionBackwardData(handle,
            &alpha,
            out.desc,
            out_dev.get(),
            weights.desc,
            wei_dev.get(),
            perf.bwd_data_algo,
            &beta,
            input.desc,
            in_dev.get(),
            nullptr,
            0);

        input.data = handle.Read<T>(in_dev, input.data.size());
        return input;
    }

    template<class T>
    void fail(float, const tensor<T>& output, const tensor<T>& weights, const mlopen::ConvolutionDescriptor& filter, int /*bias*/ = 0)
    {
        std::cout << "Backward convolution: " << std::endl;
        std::cout << "Input tensor: " << filter.GetBackwardOutputTensor(output.desc, weights.desc).ToString() << std::endl;
        std::cout << "Output tensor: " << output.desc.ToString() << std::endl;
        std::cout << "Weights tensor: " << weights.desc.ToString() << std::endl;
    }

};

template<class T>
tensor<T> get_weight_tensor(const mlopen::ConvolutionDescriptor& filter, const tensor<T>& input, const tensor<T>& out)
{
    return tensor<T>{filter.GetBackwardWeightsTensor(input.desc, out.desc)};
}

struct verify_backward_weights_conv
{
    template<class T>
    tensor<T> cpu(const tensor<T>& input, const tensor<T>& out, const mlopen::ConvolutionDescriptor& filter, int /* bias */ = 0)
    {
        auto weights = get_weight_tensor(filter, input, out);
        std::fill(weights.begin(), weights.end(), 0);

        int in_h, in_w;
        std::tie(std::ignore, std::ignore, in_h, in_w) = mlopen::tie4(input.desc.GetLengths());

        int wei_c, wei_h, wei_w;
        std::tie(std::ignore, wei_c, wei_h, wei_w) = mlopen::tie4(weights.desc.GetLengths());

        int out_n, out_c, out_h, out_w;
        std::tie(out_n, out_c, out_h, out_w) = mlopen::tie4(out.desc.GetLengths());

        par_ford(out_c, wei_c, wei_h, wei_w)([&](int w, int k, int x, int y)
        {
            double acc = 0.0;
            ford(out_n, out_h, out_w)([&](int o, int i, int j)
            {
                const int start_x = i * filter.v - filter.pad_h;
                const int start_y = j * filter.u - filter.pad_w;
                const int in_x = start_x + x;
                const int in_y = start_y + y;
                if(in_x >= 0 && in_x < in_h && in_y >= 0 && in_y < in_w) {
                    acc += input(o, k, in_x, in_y) * out(o, w, i, j);
                }
            });
            weights(w, k, x, y) = acc;
        });
        return weights;
    }

    template<class T>
    tensor<T> gpu(const tensor<T>& input, const tensor<T>& out, const mlopen::ConvolutionDescriptor& filter, int /* bias */ = 0)
    {
        auto&& handle = get_handle();
        auto weights = get_weight_tensor(filter, input, out);
        std::fill(weights.begin(), weights.end(), 0);

        auto out_dev = handle.Write(out.data);
        auto wei_dev = handle.Create<T>(weights.data.size());
        auto in_dev = handle.Write(input.data);

        std::size_t workspace_size = filter.ConvolutionBackwardWeightsGetWorkSpaceSize(out.desc, input.desc, weights.desc);

        std::vector<char> workspace(workspace_size);
        auto workspace_dev = handle.Write(workspace);

        int ret_algo_count;
        mlopenConvAlgoPerf_t perf;

        int alpha = 1, beta = 1;
        filter.FindConvBwdWeightsAlgorithm(handle,
            out.desc,
            out_dev.get(),
            input.desc,
            in_dev.get(),
            weights.desc,
            wei_dev.get(),
            1,
            &ret_algo_count,
            &perf,
            mlopenConvolutionFastest,
            workspace_dev.get(),
            workspace_size,
            0); // MD: Not performing exhaustiveSearch by default for now

        filter.ConvolutionBackwardWeights(handle,
            &alpha,
            out.desc,
            out_dev.get(),
            input.desc,
            in_dev.get(),
            perf.bwd_weights_algo,
            &beta,
            weights.desc,
            wei_dev.get(),
            workspace_dev.get(),
            workspace_size);

        weights.data = handle.Read<T>(wei_dev, weights.data.size());
        return weights;
    }

    template<class T>
    void fail(float, const tensor<T>& input, const tensor<T>& output, const mlopen::ConvolutionDescriptor& filter, int /*bias*/ = 0)
    {
        std::cout << "Backward weights convolution: " << std::endl;
        std::cout << "Input tensor: " << input.desc.ToString() << std::endl;
        std::cout << "Output tensor: " << output.desc.ToString() << std::endl;
        std::cout << "Weights tensor: " << filter.GetBackwardWeightsTensor(input.desc, output.desc).ToString() << std::endl;
    }

};

template<class T>
struct conv_driver : test_driver
{
    tensor<T> input;
    tensor<T> weights;
    mlopen::ConvolutionDescriptor filter;

    conv_driver()
    {
        add(input, "input", get_input_tensor());
        add(weights, "weights", get_weights_tensor());
        add(filter, "filter", generate_single(mlopen::ConvolutionDescriptor{0, 0}));
    }

    void run()
    {
        if (input.desc.GetLengths().at(1) == weights.desc.GetLengths().at(1))
        {
            auto out_p = verify(verify_forward_conv{}, input, weights, filter);
            for(auto& x:out_p.first) x = (long(x+1)*2) % 17; // Clamp big numbers
            verify(verify_backward_conv{}, out_p.first, weights, filter);
#if MLOPEN_USE_TINYGEMM
            verify(verify_backward_weights_conv{}, input, out_p.first, filter);
#endif
        }
    }
};

int main(int argc, const char *argv[])
{
    test_drive<conv_driver<float>>(argc, argv);
}
<|endoftext|>