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

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt 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 notices for more information.

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

#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbRadiometricMomentsImageFilter.h"

#include "otbMultiToMonoChannelExtractROI.h"

#include "itkTimeProbe.h"

namespace otb
{
namespace Wrapper
{

class LocalStatisticExtraction : public Application
{
public:
/** Standard class typedefs. */
typedef LocalStatisticExtraction     Self;
typedef Application                   Superclass;
typedef itk::SmartPointer<Self>       Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;

typedef MultiToMonoChannelExtractROI<FloatVectorImageType::InternalPixelType,FloatVectorImageType::InternalPixelType>
ExtractorFilterType;

typedef RadiometricMomentsImageFilter<FloatImageType, FloatVectorImageType>
FilterType;

/** Standard macro */
itkNewMacro(Self);

itkTypeMacro(LocalStatisticExtraction, otb::Application);

private:

void DoInit()
{
SetName("LocalStatisticExtraction");
SetDescription("Computes local statistics on every pixel of the input image selected channel");

// Documentation
SetDocName("Local Statistic Extraction");
SetDocLongDescription("This application computes local Statistics on a mono band image");
SetDocLimitations("None");
SetDocAuthors("OTB-Team");
SetDocSeeAlso("otbRadiometricMomentsImageFunction class");

AddDocTag("Statistics");
AddDocTag(Tags::FeatureExtraction);

AddParameter(ParameterType_InputImage, "in",  "Input Image");
SetParameterDescription("in", "The input image to compute the features on.");

AddParameter(ParameterType_Int,  "channel",  "Selected Channel");
SetParameterDescription("channel", "The selected channel index");
SetDefaultParameterInt("channel", 1);
SetMinimumParameterIntValue("channel", 1);

AddRAMParameter();

AddParameter(ParameterType_Int, "radius", "Neighborhood radius");
SetParameterDescription("radius", "The computational window radius.");
SetMinimumParameterIntValue("radius",1);
SetDefaultParameterInt("radius",3);

AddParameter(ParameterType_OutputImage, "out", "Feature Output Image");
SetParameterDescription("out", "Output image containing the local statistic features.");

// Doc example parameter settings
SetDocExampleParameterValue("in", "qb_RoadExtract.tif");
SetDocExampleParameterValue("channel", "1");
SetDocExampleParameterValue("radius", "3");
SetDocExampleParameterValue("out", "Statistics.tif");
}

void DoUpdateParameters()
{
  // Nothing to do here : all parameters are independent
}

void DoExecute()
{
  FloatVectorImageType::Pointer inImage = GetParameterImage("in");
  inImage->UpdateOutputInformation();
  int nbChan = inImage->GetNumberOfComponentsPerPixel();

  if( GetParameterInt("channel") > nbChan )
    {
    otbAppLogCRITICAL("Selected band is not available...");
    return;
    }

  m_ExtractorFilter = ExtractorFilterType::New();
  m_ExtractorFilter->SetInput(inImage);
  m_ExtractorFilter->SetStartX(inImage->GetLargestPossibleRegion().GetIndex(0));
  m_ExtractorFilter->SetStartY(inImage->GetLargestPossibleRegion().GetIndex(1));
  m_ExtractorFilter->SetSizeX(inImage->GetLargestPossibleRegion().GetSize(0));
  m_ExtractorFilter->SetSizeY(inImage->GetLargestPossibleRegion().GetSize(1));
  m_ExtractorFilter->SetChannel(GetParameterInt("channel"));
  m_ExtractorFilter->UpdateOutputInformation();

  m_Filter = FilterType::New();
  m_Filter->SetInput(m_ExtractorFilter->GetOutput());
  m_Filter->SetRadius(GetParameterInt("radius"));
  m_Filter->UpdateOutputInformation();

  SetParameterOutputImage("out", m_Filter->GetOutput());

}
ExtractorFilterType::Pointer m_ExtractorFilter;
FilterType::Pointer          m_Filter;

};
}
}

OTB_APPLICATION_EXPORT(otb::Wrapper::LocalStatisticExtraction)
<commit_msg>DOC: LocalStatisticExtraction application: description of the 4 extracted statistical moments<commit_after>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt 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 notices for more information.

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

#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "otbRadiometricMomentsImageFilter.h"

#include "otbMultiToMonoChannelExtractROI.h"

#include "itkTimeProbe.h"

namespace otb
{
namespace Wrapper
{

class LocalStatisticExtraction : public Application
{
public:
/** Standard class typedefs. */
typedef LocalStatisticExtraction     Self;
typedef Application                   Superclass;
typedef itk::SmartPointer<Self>       Pointer;
typedef itk::SmartPointer<const Self> ConstPointer;

typedef MultiToMonoChannelExtractROI<FloatVectorImageType::InternalPixelType,FloatVectorImageType::InternalPixelType>
ExtractorFilterType;

typedef RadiometricMomentsImageFilter<FloatImageType, FloatVectorImageType>
FilterType;

/** Standard macro */
itkNewMacro(Self);

itkTypeMacro(LocalStatisticExtraction, otb::Application);

private:

void DoInit()
{
SetName("LocalStatisticExtraction");
SetDescription("Computes local statistical moments on every pixel in the selected channel of the input image");

// Documentation
SetDocName("Local Statistic Extraction");
SetDocLongDescription("This application computes the 4 local statistical moments on every pixel in the selected channel of the input image, over a specified neighborhood. The output image is multi band with one statistical moment (feature) per band. Thus, the 4 output features are the Mean, the Variance, the Skewness and the Kurtosis. They are provided in this exact order in the output image.");
SetDocLimitations("None");
SetDocAuthors("OTB-Team");
SetDocSeeAlso("otbRadiometricMomentsImageFunction class");

AddDocTag("Statistics");
AddDocTag(Tags::FeatureExtraction);

AddParameter(ParameterType_InputImage, "in",  "Input Image");
SetParameterDescription("in", "The input image to compute the features on.");

AddParameter(ParameterType_Int,  "channel",  "Selected Channel");
SetParameterDescription("channel", "The selected channel index");
SetDefaultParameterInt("channel", 1);
SetMinimumParameterIntValue("channel", 1);

AddRAMParameter();

AddParameter(ParameterType_Int, "radius", "Neighborhood radius");
SetParameterDescription("radius", "The computational window radius.");
SetMinimumParameterIntValue("radius",1);
SetDefaultParameterInt("radius",3);

AddParameter(ParameterType_OutputImage, "out", "Feature Output Image");
SetParameterDescription("out", "Output image containing the local statistical moments.");

// Doc example parameter settings
SetDocExampleParameterValue("in", "qb_RoadExtract.tif");
SetDocExampleParameterValue("channel", "1");
SetDocExampleParameterValue("radius", "3");
SetDocExampleParameterValue("out", "Statistics.tif");
}

void DoUpdateParameters()
{
  // Nothing to do here : all parameters are independent
}

void DoExecute()
{
  FloatVectorImageType::Pointer inImage = GetParameterImage("in");
  inImage->UpdateOutputInformation();
  int nbChan = inImage->GetNumberOfComponentsPerPixel();

  if( GetParameterInt("channel") > nbChan )
    {
    otbAppLogCRITICAL("Selected band is not available...");
    return;
    }

  m_ExtractorFilter = ExtractorFilterType::New();
  m_ExtractorFilter->SetInput(inImage);
  m_ExtractorFilter->SetStartX(inImage->GetLargestPossibleRegion().GetIndex(0));
  m_ExtractorFilter->SetStartY(inImage->GetLargestPossibleRegion().GetIndex(1));
  m_ExtractorFilter->SetSizeX(inImage->GetLargestPossibleRegion().GetSize(0));
  m_ExtractorFilter->SetSizeY(inImage->GetLargestPossibleRegion().GetSize(1));
  m_ExtractorFilter->SetChannel(GetParameterInt("channel"));
  m_ExtractorFilter->UpdateOutputInformation();

  m_Filter = FilterType::New();
  m_Filter->SetInput(m_ExtractorFilter->GetOutput());
  m_Filter->SetRadius(GetParameterInt("radius"));
  m_Filter->UpdateOutputInformation();

  SetParameterOutputImage("out", m_Filter->GetOutput());

}
ExtractorFilterType::Pointer m_ExtractorFilter;
FilterType::Pointer          m_Filter;

};
}
}

OTB_APPLICATION_EXPORT(otb::Wrapper::LocalStatisticExtraction)
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the demonstration applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "squircle.h"

#include <QtQuick/qquickwindow.h>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLContext>

//! [7]
Squircle::Squircle()
    : m_program(0)
    , m_t(0)
    , m_thread_t(0)
{
}
//! [7]

//! [8]
void Squircle::setT(qreal t)
{
    if (t == m_t)
        return;
    m_t = t;
    emit tChanged();
    if (window())
        window()->update();
}
//! [8]

//! [1]
void Squircle::itemChange(ItemChange change, const ItemChangeData &)
{
    // The ItemSceneChange event is sent when we are first attached to a window.
    if (change == ItemSceneChange) {
        QQuickWindow *win = window();
        if (!win)
            return;
//! [1]

        // Connect the beforeRendering signal to our paint function.
        // Since this call is executed on the rendering thread it must be
        // a Qt::DirectConnection
//! [2]
        connect(win, SIGNAL(beforeRendering()), this, SLOT(paint()), Qt::DirectConnection);
        connect(win, SIGNAL(beforeSynchronizing()), this, SLOT(sync()), Qt::DirectConnection);
//! [2]

        // If we allow QML to do the clearing, they would clear what we paint
        // and nothing would show.
//! [3]
        win->setClearBeforeRendering(false);
    }
}

//! [3] //! [4]
void Squircle::paint()
{
    if (!m_program) {
        m_program = new QOpenGLShaderProgram();
        m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
                                           "attribute highp vec4 vertices;"
                                           "varying highp vec2 coords;"
                                           "void main() {"
                                           "    gl_Position = vertices;"
                                           "    coords = vertices.xy;"
                                           "}");
        m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
                                           "uniform lowp float t;"
                                           "varying highp vec2 coords;"
                                           "void main() {"
                                           "    lowp float i = 1. - (pow(coords.x, 4.) + pow(coords.y, 4.));"
                                           "    i = smoothstep(t - 0.8, t + 0.8, i);"
                                           "    i = floor(i * 20.) / 20.;"
                                           "    gl_FragColor = vec4(coords * .5 + .5, i, i);"
                                           "}");

        m_program->bindAttributeLocation("vertices", 0);
        m_program->link();

        connect(window()->openglContext(), SIGNAL(aboutToBeDestroyed()),
                this, SLOT(cleanup()), Qt::DirectConnection);
    }
//! [4] //! [5]
    m_program->bind();

    m_program->enableAttributeArray(0);

    float values[] = {
        -1, -1,
        1, -1,
        -1, 1,
        1, 1
    };
    m_program->setAttributeArray(0, GL_FLOAT, values, 2);
    m_program->setUniformValue("t", (float) m_thread_t);

    glViewport(0, 0, window()->width(), window()->height());

    glDisable(GL_DEPTH_TEST);

    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    m_program->disableAttributeArray(0);
    m_program->release();
}
//! [5]

//! [6]
void Squircle::cleanup()
{
    if (m_program) {
        delete m_program;
        m_program = 0;
    }
}
//! [6]

//! [9]
void Squircle::sync()
{
    m_thread_t = m_t;
}
//! [9]

<commit_msg>Fixed invalid GLSL in openglunderqml example.<commit_after>/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the demonstration applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "squircle.h"

#include <QtQuick/qquickwindow.h>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLContext>

//! [7]
Squircle::Squircle()
    : m_program(0)
    , m_t(0)
    , m_thread_t(0)
{
}
//! [7]

//! [8]
void Squircle::setT(qreal t)
{
    if (t == m_t)
        return;
    m_t = t;
    emit tChanged();
    if (window())
        window()->update();
}
//! [8]

//! [1]
void Squircle::itemChange(ItemChange change, const ItemChangeData &)
{
    // The ItemSceneChange event is sent when we are first attached to a window.
    if (change == ItemSceneChange) {
        QQuickWindow *win = window();
        if (!win)
            return;
//! [1]

        // Connect the beforeRendering signal to our paint function.
        // Since this call is executed on the rendering thread it must be
        // a Qt::DirectConnection
//! [2]
        connect(win, SIGNAL(beforeRendering()), this, SLOT(paint()), Qt::DirectConnection);
        connect(win, SIGNAL(beforeSynchronizing()), this, SLOT(sync()), Qt::DirectConnection);
//! [2]

        // If we allow QML to do the clearing, they would clear what we paint
        // and nothing would show.
//! [3]
        win->setClearBeforeRendering(false);
    }
}

//! [3] //! [4]
void Squircle::paint()
{
    if (!m_program) {
        m_program = new QOpenGLShaderProgram();
        m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
                                           "attribute highp vec4 vertices;"
                                           "varying highp vec2 coords;"
                                           "void main() {"
                                           "    gl_Position = vertices;"
                                           "    coords = vertices.xy;"
                                           "}");
        m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
                                           "uniform lowp float t;"
                                           "varying highp vec2 coords;"
                                           "void main() {"
                                           "    lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));"
                                           "    i = smoothstep(t - 0.8, t + 0.8, i);"
                                           "    i = floor(i * 20.) / 20.;"
                                           "    gl_FragColor = vec4(coords * .5 + .5, i, i);"
                                           "}");

        m_program->bindAttributeLocation("vertices", 0);
        m_program->link();

        connect(window()->openglContext(), SIGNAL(aboutToBeDestroyed()),
                this, SLOT(cleanup()), Qt::DirectConnection);
    }
//! [4] //! [5]
    m_program->bind();

    m_program->enableAttributeArray(0);

    float values[] = {
        -1, -1,
        1, -1,
        -1, 1,
        1, 1
    };
    m_program->setAttributeArray(0, GL_FLOAT, values, 2);
    m_program->setUniformValue("t", (float) m_thread_t);

    glViewport(0, 0, window()->width(), window()->height());

    glDisable(GL_DEPTH_TEST);

    glClearColor(0, 0, 0, 1);
    glClear(GL_COLOR_BUFFER_BIT);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    m_program->disableAttributeArray(0);
    m_program->release();
}
//! [5]

//! [6]
void Squircle::cleanup()
{
    if (m_program) {
        delete m_program;
        m_program = 0;
    }
}
//! [6]

//! [9]
void Squircle::sync()
{
    m_thread_t = m_t;
}
//! [9]

<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2012-2013 Danny Y., Rapptz

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

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

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

#ifndef GEARS_CONCEPTS_CONTAINER_HPP
#define GEARS_CONCEPTS_CONTAINER_HPP

#include "iterator.hpp"

namespace gears {
namespace concepts {
namespace detail {
struct is_container {
    template<typename C,
             typename T  = typename Bare<C>::value_type,
             typename R  = typename Bare<C>::reference,
             typename Cr = typename Bare<C>::const_reference,
             typename It = typename Bare<C>::iterator,
             typename Ci = typename Bare<C>::const_iterator,
             typename Dt = typename Bare<C>::difference_type,
             typename St = typename Bare<C>::size_type,
             typename B  = decltype(std::declval<LRef<C>>().begin()),
             typename E  = decltype(std::declval<LRef<C>>().end()),
             typename Cb = decltype(std::declval<LRef<C>>().cbegin()),
             typename Ce = decltype(std::declval<LRef<C>>().cend()),
             typename Sw = decltype(std::declval<LRef<C>>().swap(std::declval<LRef<C>>())),
             typename Si = decltype(std::declval<LRef<C>>().size()),
             typename Ms = decltype(std::declval<LRef<C>>().max_size()),
             typename Em = decltype(std::declval<LRef<C>>().empty()),
             TrueIf<ContextualBool<Em>,         std::is_same<Si, St>,
                    std::is_same<Ms, St>,       std::is_same<Cb, Ci>,
                    std::is_same<Ce, Ci>,       std::is_convertible<It, Ci>,
                    std::is_convertible<B, Ci>, std::is_convertible<E, Ci>,
                    ForwardIterator<It>>...>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

struct is_forward_list {
    template<typename C,
             typename T = typename Bare<C>::value_type,
             typename It = typename Bare<C>::const_iterator,
             typename Ia = decltype(std::declval<LRef<C>>().insert_after(std::declval<It&>(), std::declval<T&>())),
             typename Em = decltype(std::declval<LRef<C>>().emplace_after(std::declval<It&>())),
             typename Ea = decltype(std::declval<LRef<C>>().erase_after(std::declval<It&>())),
             typename Sa = decltype(std::declval<LRef<C>>().splice_after(std::declval<It&>(), std::declval<LRef<C>>()))>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

struct is_reversible {
    template<typename C,
             typename Ri = typename Bare<C>::reverse_iterator,
             typename Cr = typename Bare<C>::const_reverse_iterator,
             typename Rb = decltype(std::declval<LRef<C>>().rbegin()),
             typename Re = decltype(std::declval<LRef<C>>().rend()),
             typename Cb = decltype(std::declval<LRef<C>>().crbegin()),
             typename Ce = decltype(std::declval<LRef<C>>().crend()),
             TrueIf<std::is_convertible<Ri, Cr>, std::is_convertible<Rb, Cr>,
                    std::is_convertible<Re, Cr>, std::is_same<Cb, Cr>,
                    std::is_same<Ce, Cr>>...>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

struct is_allocator_aware {
    template<typename C,
             typename A = typename Bare<C>::allocator_type,
             typename G = decltype(std::declval<LRef<C>>().get_allocator()),
             TrueIf<std::is_same<A, G>>...>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

struct is_sequence_container {
    template<typename C,
             typename B = Bare<C>,
             typename T  = typename B::value_type,
             typename St = typename B::size_type,
             typename It = typename B::iterator,
             typename C1 = decltype(B(St(), std::declval<LRef<T>>())),
             typename C2 = decltype(B(std::declval<It&>(), std::declval<It&>())),
             typename E1 = decltype(std::declval<B&>().emplace(std::declval<It&>())),
             typename E2 = decltype(std::declval<B&>().emplace(std::declval<It&>(), std::declval<LRef<T>>())),
             typename I  = decltype(std::declval<B&>().insert(std::declval<It&>(), St(), std::declval<LRef<T>>())),
             typename I2 = decltype(std::declval<B&>().insert(std::declval<It&>(), std::declval<It&>(), std::declval<It&>())),
             typename E3 = decltype(std::declval<B&>().erase(std::declval<It&>())),
             typename E4 = decltype(std::declval<B&>().erase(std::declval<It&>(), std::declval<It&>())),
             typename Cl = decltype(std::declval<B&>().clear()),
             typename A1 = decltype(std::declval<B&>().assign(std::declval<It&>(), std::declval<It&>())),
             typename A2 = decltype(std::declval<B&>().assign(St(), std::declval<LRef<T>>()))>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

struct is_array_sequence {
    template<typename C>
    static auto test(int) -> decltype(std::declval<LRef<C>>().fill(0), std::true_type{}) {}
    template<typename>
    static std::false_type test(...);
};

struct is_associative_container {
    template<typename C,
             typename B  = Bare<C>,
             typename Kt = typename B::key_type,
             typename T  = typename B::value_type,
             typename It = typename B::iterator,
             typename Kc = typename B::key_compare,
             typename Vc = typename B::value_compare,
             typename St = typename B::size_type,
             typename KC = decltype(std::declval<B&>().key_comp()),
             typename VC = decltype(std::declval<B&>().value_comp()),
             typename Em = decltype(std::declval<B&>().emplace()),
             typename Eh = decltype(std::declval<B&>().emplace_hint(std::declval<It&>())),
             typename I1 = decltype(std::declval<B&>().insert(std::declval<T&>())),
             typename I2 = decltype(std::declval<B&>().insert(std::declval<It&>(), std::declval<T&>())),
             typename I3 = decltype(std::declval<B&>().insert(std::declval<It&>(), std::declval<It&>())),
             typename E1 = decltype(std::declval<B&>().erase(std::declval<Kt&>())),
             typename E2 = decltype(std::declval<B&>().erase(std::declval<It&>())),
             typename E3 = decltype(std::declval<B&>().erase(std::declval<It&>(), std::declval<It&>())),
             typename Cl = decltype(std::declval<B&>().clear()),
             typename Fi = decltype(std::declval<B&>().find(std::declval<Kt>())),
             typename Co = decltype(std::declval<B&>().count(std::declval<Kt>())),
             typename Lb = decltype(std::declval<B&>().lower_bound(std::declval<Kt>())),
             typename Ub = decltype(std::declval<B&>().upper_bound(std::declval<Kt>())),
             typename Er = decltype(std::declval<B&>().equal_range(std::declval<Kt>())),
             TrueIf<std::is_constructible<B, It, It, Kc>,
                    std::is_constructible<B, It, It>,
                    std::is_convertible<I2, It>,
                    std::is_convertible<E1, St>,
                    std::is_convertible<E2, It>,
                    std::is_convertible<E3, It>,
                    std::is_convertible<Fi, It>,
                    std::is_convertible<Co, St>,
                    std::is_convertible<Lb, It>,
                    std::is_convertible<Ub, It>>...>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};
} // detail

template<typename T>
struct Container : And<Or<TraitOf<detail::is_container, T>,
                          TraitOf<detail::is_forward_list, T>>,
                       Regular<T>,
                       Destructible<T>> {};

template<typename T>
struct ReversibleContainer : And<Container<T>, TraitOf<detail::is_reversible, T>> {};

template<typename T>
struct AllocatorAwareContainer : And<Container<T>, TraitOf<detail::is_allocator_aware, T>> {};

template<typename T>
struct SequenceContainer : And<Container<T>, Or<TraitOf<detail::is_sequence_container, T>,
                                                TraitOf<detail::is_array_sequence, T>,
                                                TraitOf<detail::is_forward_list, T>>> {};

template<typename T>
struct AssociativeContainer : And<Container<T>, TraitOf<detail::is_associative_container, T>> {};
} // concepts
} // gears

#endif // GEARS_CONCEPTS_CONTAINER_HPP<commit_msg>Finish documenting container concepts<commit_after>// The MIT License (MIT)

// Copyright (c) 2012-2013 Danny Y., Rapptz

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

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

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

#ifndef GEARS_CONCEPTS_CONTAINER_HPP
#define GEARS_CONCEPTS_CONTAINER_HPP

#include "iterator.hpp"

namespace gears {
namespace concepts {
namespace detail {
struct is_container {
    template<typename C,
             typename T  = typename Bare<C>::value_type,
             typename R  = typename Bare<C>::reference,
             typename Cr = typename Bare<C>::const_reference,
             typename It = typename Bare<C>::iterator,
             typename Ci = typename Bare<C>::const_iterator,
             typename Dt = typename Bare<C>::difference_type,
             typename St = typename Bare<C>::size_type,
             typename B  = decltype(std::declval<LRef<C>>().begin()),
             typename E  = decltype(std::declval<LRef<C>>().end()),
             typename Cb = decltype(std::declval<LRef<C>>().cbegin()),
             typename Ce = decltype(std::declval<LRef<C>>().cend()),
             typename Sw = decltype(std::declval<LRef<C>>().swap(std::declval<LRef<C>>())),
             typename Si = decltype(std::declval<LRef<C>>().size()),
             typename Ms = decltype(std::declval<LRef<C>>().max_size()),
             typename Em = decltype(std::declval<LRef<C>>().empty()),
             TrueIf<ContextualBool<Em>,         std::is_same<Si, St>,
                    std::is_same<Ms, St>,       std::is_same<Cb, Ci>,
                    std::is_same<Ce, Ci>,       std::is_convertible<It, Ci>,
                    std::is_convertible<B, Ci>, std::is_convertible<E, Ci>,
                    ForwardIterator<It>>...>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

struct is_forward_list {
    template<typename C,
             typename T = typename Bare<C>::value_type,
             typename It = typename Bare<C>::const_iterator,
             typename Ia = decltype(std::declval<LRef<C>>().insert_after(std::declval<It&>(), std::declval<T&>())),
             typename Em = decltype(std::declval<LRef<C>>().emplace_after(std::declval<It&>())),
             typename Ea = decltype(std::declval<LRef<C>>().erase_after(std::declval<It&>())),
             typename Sa = decltype(std::declval<LRef<C>>().splice_after(std::declval<It&>(), std::declval<LRef<C>>()))>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

struct is_reversible {
    template<typename C,
             typename Ri = typename Bare<C>::reverse_iterator,
             typename Cr = typename Bare<C>::const_reverse_iterator,
             typename Rb = decltype(std::declval<LRef<C>>().rbegin()),
             typename Re = decltype(std::declval<LRef<C>>().rend()),
             typename Cb = decltype(std::declval<LRef<C>>().crbegin()),
             typename Ce = decltype(std::declval<LRef<C>>().crend()),
             TrueIf<std::is_convertible<Ri, Cr>, std::is_convertible<Rb, Cr>,
                    std::is_convertible<Re, Cr>, std::is_same<Cb, Cr>,
                    std::is_same<Ce, Cr>>...>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

struct is_allocator_aware {
    template<typename C,
             typename A = typename Bare<C>::allocator_type,
             typename G = decltype(std::declval<LRef<C>>().get_allocator()),
             TrueIf<std::is_same<A, G>>...>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

struct is_sequence_container {
    template<typename C,
             typename B = Bare<C>,
             typename T  = typename B::value_type,
             typename St = typename B::size_type,
             typename It = typename B::iterator,
             typename C1 = decltype(B(St(), std::declval<LRef<T>>())),
             typename C2 = decltype(B(std::declval<It&>(), std::declval<It&>())),
             typename E1 = decltype(std::declval<B&>().emplace(std::declval<It&>())),
             typename E2 = decltype(std::declval<B&>().emplace(std::declval<It&>(), std::declval<LRef<T>>())),
             typename I  = decltype(std::declval<B&>().insert(std::declval<It&>(), St(), std::declval<LRef<T>>())),
             typename I2 = decltype(std::declval<B&>().insert(std::declval<It&>(), std::declval<It&>(), std::declval<It&>())),
             typename E3 = decltype(std::declval<B&>().erase(std::declval<It&>())),
             typename E4 = decltype(std::declval<B&>().erase(std::declval<It&>(), std::declval<It&>())),
             typename Cl = decltype(std::declval<B&>().clear()),
             typename A1 = decltype(std::declval<B&>().assign(std::declval<It&>(), std::declval<It&>())),
             typename A2 = decltype(std::declval<B&>().assign(St(), std::declval<LRef<T>>()))>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};

struct is_array_sequence {
    template<typename C>
    static auto test(int) -> decltype(std::declval<LRef<C>>().fill(0), std::true_type{}) {}
    template<typename>
    static std::false_type test(...);
};

struct is_associative_container {
    template<typename C,
             typename B  = Bare<C>,
             typename Kt = typename B::key_type,
             typename T  = typename B::value_type,
             typename It = typename B::iterator,
             typename Kc = typename B::key_compare,
             typename Vc = typename B::value_compare,
             typename St = typename B::size_type,
             typename KC = decltype(std::declval<B&>().key_comp()),
             typename VC = decltype(std::declval<B&>().value_comp()),
             typename Em = decltype(std::declval<B&>().emplace()),
             typename Eh = decltype(std::declval<B&>().emplace_hint(std::declval<It&>())),
             typename I1 = decltype(std::declval<B&>().insert(std::declval<T&>())),
             typename I2 = decltype(std::declval<B&>().insert(std::declval<It&>(), std::declval<T&>())),
             typename I3 = decltype(std::declval<B&>().insert(std::declval<It&>(), std::declval<It&>())),
             typename E1 = decltype(std::declval<B&>().erase(std::declval<Kt&>())),
             typename E2 = decltype(std::declval<B&>().erase(std::declval<It&>())),
             typename E3 = decltype(std::declval<B&>().erase(std::declval<It&>(), std::declval<It&>())),
             typename Cl = decltype(std::declval<B&>().clear()),
             typename Fi = decltype(std::declval<B&>().find(std::declval<Kt>())),
             typename Co = decltype(std::declval<B&>().count(std::declval<Kt>())),
             typename Lb = decltype(std::declval<B&>().lower_bound(std::declval<Kt>())),
             typename Ub = decltype(std::declval<B&>().upper_bound(std::declval<Kt>())),
             typename Er = decltype(std::declval<B&>().equal_range(std::declval<Kt>())),
             TrueIf<std::is_constructible<B, It, It, Kc>,
                    std::is_constructible<B, It, It>,
                    std::is_convertible<I2, It>,
                    std::is_convertible<E1, St>,
                    std::is_convertible<E2, It>,
                    std::is_convertible<E3, It>,
                    std::is_convertible<Fi, It>,
                    std::is_convertible<Co, St>,
                    std::is_convertible<Lb, It>,
                    std::is_convertible<Ub, It>>...>
    static std::true_type test(int);
    template<typename...>
    static std::false_type test(...);
};
} // detail

/**
 * @defgroup container_concepts Container concepts submodule
 * @ingroup concepts
 *
 * @brief Concepts involving the standard C++ containers.
 *
 * @details A collection of concepts that deal with the semantics of
 * what entails a C++ standard container or a type that meets similar
 * requirements.
 */

/**
 * @ingroup container_concepts
 * @brief Checks if a type is a container.
 * @details A Unary concept that checks if a type is a container.
 *
 * A type that meets this concept must provide the following
 * typedefs:
 *
 * - `value_type`
 * - `reference`
 * - `const_reference`
 * - `iterator`
 * - `const_iterator`
 * - `difference_type`
 * - `size_type`
 *
 * It must meet the following concepts:
 *
 * - Destructible
 * - Regular
 * - EqualityComparable
 * - Copyable
 *
 * The following expressions must be valid:
 *
 * @code
 * auto a = x.begin();    // returns iterator or const_iterator
 * auto b = x.end();     //  returns iterator or const_iterator
 * auto c = x.cbegin()  //   returns const_iterator
 * auto d = x.cend()   //    returns const_iterator
 * x.swap(y);
 * auto e = x.size()     //  returns size_type
 * auto f = x.max_size(); // returns size_type
 * bool g = x.empty();
 * @endcode
 *
 * Where x and y are instances of T.
 *
 * There is a specialisation for `std::forward_list<T>` to make it a valid container.
 *
 * @tparam T Type to check.
 */
template<typename T>
struct Container : And<Or<TraitOf<detail::is_container, T>,
                          TraitOf<detail::is_forward_list, T>>,
                       Regular<T>,
                       EqualityComparable<T>,
                       Copyable<T>,
                       Destructible<T>> {};

/**
 * @ingroup container_concepts
 * @brief Checks if a type is a container that can be reversed.
 * @details A Unary concept that checks if a type is a container that can be reversed.
 *
 * It must meet the Container concept. In addition to that, it must have the following
 * typedefs defined:
 *
 * - `reverse_iterator`
 * - `const_reverse_iterator`
 *
 * and the following expressions must be valid:
 *
 * @code
 * auto a = x.rbegin();    // returns reverse_iterator or const_reverse_iterator
 * auto b = x.rend();      // returns reverse_iterator or const_reverse_iterator
 * auto c = x.crbegin();   // returns const_reverse_iterator
 * auto d = x.crend();     // returns const_reverse_iterator
 * @endcode
 *
 * Where x is an instance of T.
 *
 * @tparam T Type to check
 */
template<typename T>
struct ReversibleContainer : And<Container<T>, TraitOf<detail::is_reversible, T>> {};

/**
 * @ingroup container_concepts
 * @brief Checks if a type is a container with an allocator.
 * @details A Unary concept that checks if a type is a container that has an allocator.
 *
 * It must meet the Container concept. In addition to that, it must have the
 * `allocator_type` typedef defined and the following expression must be valid:
 *
 * @code
 * auto a = x.get_allocator(); // returns allocator_type
 * @endcode
 *
 * Where x is an instance of T.
 *
 * @tparam T Type to check
 */
template<typename T>
struct AllocatorAwareContainer : And<Container<T>, TraitOf<detail::is_allocator_aware, T>> {};

/**
 * @ingroup container_concepts
 * @brief Checks if a type is a sequence container.
 * @details A Unary concept that checks if a type is a sequence container.
 *
 * It must meet the Container concept. In addition to that, the following expressions
 * must be valid:
 *
 * @code
 * T x(size_type{}, value_type{});
 * T y(iterator{}, iterator{});
 * iterator a = x.emplace(iterator{}, args...);
 * iterator b = x.insert(iterator{}, value_type{});
 * iterator c = x.insert(iterator{}, size_type{}, value_type{});
 * iterator d = x.insert(iterator{}, iterator{}, iterator{});
 * iterator e = x.erase(const_iterator{});
 * iterator f = x.erase(const_iterator{}, const_iterator{});
 * x.clear();
 * x.assign(iterator{}, iterator{});
 * x.assign(size_type{}, value_type{});
 * @endcode
 *
 * @tparam T Type to check
 */
template<typename T>
struct SequenceContainer : And<Container<T>, Or<TraitOf<detail::is_sequence_container, T>,
                                                TraitOf<detail::is_array_sequence, T>,
                                                TraitOf<detail::is_forward_list, T>>> {};

/**
 * @ingroup container_concepts
 * @brief Checks if a type is an associative container.
 * @details A Unary concept that checks if a type is an associative container.
 *
 * It must meet the Container concept. In addition, the following typedefs must
 * be defined:
 *
 * - `key_type`
 * - `key_compare`
 * - `value_compare`
 *
 * and the following expressions must be valid:
 *
 * @code
 * T x;
 * key_compare kc = x.key_comp();
 * value_compare vc = x.value_comp();
 * iterator a = x.emplace(args...);
 * iterator b = x.emplace_hint(iterator{}, args...);
 * iterator c = x.insert(iterator{});
 * iterator d = x.insert(iterator{}, value_type{});
 * iterator e = x.insert(iterator{}, iterator{});
 * iterator f = x.erase(const_iterator{});
 * iterator g = x.erase(const_iterator{}, const_iterator{});
 * size_type h = x.erase(key_type{});
 * x.clear();
 * iterator i = x.find(key_type{});
 * size_type j = x.count(key_type{});
 * iterator k = x.lower_bound(key_type{});
 * iterator l = x.upper_bound(key_type{});
 * std::pair<iterator, iterator> m = x.equal_range(key_type{});
 * @endcode
 *
 * @tparam T Type to check.
 */
template<typename T>
struct AssociativeContainer : And<Container<T>, TraitOf<detail::is_associative_container, T>> {};
} // concepts
} // gears

#endif // GEARS_CONCEPTS_CONTAINER_HPP<|endoftext|>


<commit_before>// Copyright (c) 2011-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "peertablemodel.h"

#include "clientmodel.h"

#include "net.h"
#include "sync.h"

#include <QDebug>
#include <QList>
#include <QTimer>

bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const
{
    const CNodeStats *pLeft = &(left.nodestats);
    const CNodeStats *pRight = &(right.nodestats);

    if (order == Qt::DescendingOrder)
        std::swap(pLeft, pRight);

    switch(column)
    {
    case PeerTableModel::Address:
        return pLeft->addrName.compare(pRight->addrName) < 0;
    case PeerTableModel::Subversion:
        return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
    case PeerTableModel::Height:
        return pLeft->nStartingHeight < pRight->nStartingHeight;
    }

    return false;
}

// private implementation
class PeerTablePriv
{
public:
    /** Local cache of peer information */
    QList<CNodeCombinedStats> cachedNodeStats;
    /** Column to sort nodes by */
    int sortColumn;
    /** Order (ascending or descending) to sort nodes by */
    Qt::SortOrder sortOrder;
    /** Index of rows by node ID */
    std::map<NodeId, int> mapNodeRows;

    /** Pull a full list of peers from vNodes into our cache */
    void refreshPeers() {
        TRY_LOCK(cs_vNodes, lockNodes);
        {
            if (!lockNodes)
            {
                // skip the refresh if we can't immediately get the lock
                return;
            }
            cachedNodeStats.clear();
#if QT_VERSION >= 0x040700
            cachedNodeStats.reserve(vNodes.size());
#endif
            BOOST_FOREACH(CNode* pnode, vNodes)
            {
                CNodeCombinedStats stats;
                stats.statestats.nMisbehavior = -1;
                pnode->copyStats(stats.nodestats);
                cachedNodeStats.append(stats);
            }
        }

        // if we can, retrieve the CNodeStateStats for each node.
        TRY_LOCK(cs_main, lockMain);
        {
            if (lockMain)
            {
                BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
                {
                    GetNodeStateStats(stats.nodestats.nodeid, stats.statestats);
                }
            }
        }

        if (sortColumn >= 0)
            // sort cacheNodeStats (use stable sort to prevent rows jumping around unneceesarily)
            qStableSort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));

        // build index map
        mapNodeRows.clear();
        int row = 0;
        BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
        {
            mapNodeRows.insert(std::pair<NodeId, int>(stats.nodestats.nodeid, row++));
        }
    }

    int size()
    {
        return cachedNodeStats.size();
    }

    CNodeCombinedStats *index(int idx)
    {
        if(idx >= 0 && idx < cachedNodeStats.size()) {
            return &cachedNodeStats[idx];
        }
        else
        {
            return 0;
        }
    }

};

PeerTableModel::PeerTableModel(ClientModel *parent) :
    QAbstractTableModel(parent),clientModel(parent),timer(0)
{
    columns << tr("Address") << tr("User Agent") << tr("Start Height");
    priv = new PeerTablePriv();
    // default to unsorted
    priv->sortColumn = -1;

    // set up timer for auto refresh
    timer = new QTimer();
    connect(timer, SIGNAL(timeout()), SLOT(refresh()));

    // load initial data
    refresh();
}

void PeerTableModel::startAutoRefresh(int msecs)
{
    timer->setInterval(1000);
    timer->start();
}

void PeerTableModel::stopAutoRefresh()
{
    timer->stop();
}

int PeerTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int PeerTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return 3;
}

QVariant PeerTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    CNodeCombinedStats *rec = static_cast<CNodeCombinedStats*>(index.internalPointer());

    if(role == Qt::DisplayRole)
    {
        switch(index.column())
        {
        case Address:
            return QVariant(rec->nodestats.addrName.c_str());
        case Subversion:
            return QVariant(rec->nodestats.cleanSubVer.c_str());
        case Height:
            return rec->nodestats.nStartingHeight;
        }
    }
    return QVariant();
}

QVariant PeerTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole && section < columns.size())
        {
            return columns[section];
        }
    }
    return QVariant();
}

Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return 0;

    Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    return retval;
}

QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    CNodeCombinedStats *data = priv->index(row);

    if (data)
    {
        return createIndex(row, column, data);
    }
    else
    {
        return QModelIndex();
    }
}

const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) {
    return priv->index(idx);
}

void PeerTableModel::refresh()
{
    emit layoutAboutToBeChanged();
    priv->refreshPeers();
    emit layoutChanged();
}

int PeerTableModel::getRowByNodeId(NodeId nodeid)
{
    std::map<NodeId, int>::iterator it = priv->mapNodeRows.find(nodeid);
    if (it == priv->mapNodeRows.end())
        return -1;

    return it->second;
}

void PeerTableModel::sort(int column, Qt::SortOrder order)
{
    priv->sortColumn = column;
    priv->sortOrder = order;
    refresh();
}
<commit_msg>qt: PeerTableModel: Fix potential deadlock. #4296<commit_after>// Copyright (c) 2011-2013 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "peertablemodel.h"

#include "clientmodel.h"

#include "net.h"
#include "sync.h"

#include <QDebug>
#include <QList>
#include <QTimer>

bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const
{
    const CNodeStats *pLeft = &(left.nodestats);
    const CNodeStats *pRight = &(right.nodestats);

    if (order == Qt::DescendingOrder)
        std::swap(pLeft, pRight);

    switch(column)
    {
    case PeerTableModel::Address:
        return pLeft->addrName.compare(pRight->addrName) < 0;
    case PeerTableModel::Subversion:
        return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
    case PeerTableModel::Height:
        return pLeft->nStartingHeight < pRight->nStartingHeight;
    }

    return false;
}

// private implementation
class PeerTablePriv
{
public:
    /** Local cache of peer information */
    QList<CNodeCombinedStats> cachedNodeStats;
    /** Column to sort nodes by */
    int sortColumn;
    /** Order (ascending or descending) to sort nodes by */
    Qt::SortOrder sortOrder;
    /** Index of rows by node ID */
    std::map<NodeId, int> mapNodeRows;

    /** Pull a full list of peers from vNodes into our cache */
    void refreshPeers() {
        {
            TRY_LOCK(cs_vNodes, lockNodes);
            if (!lockNodes)
            {
                // skip the refresh if we can't immediately get the lock
                return;
            }
            cachedNodeStats.clear();
#if QT_VERSION >= 0x040700
            cachedNodeStats.reserve(vNodes.size());
#endif
            BOOST_FOREACH(CNode* pnode, vNodes)
            {
                CNodeCombinedStats stats;
                stats.statestats.nMisbehavior = -1;
                pnode->copyStats(stats.nodestats);
                cachedNodeStats.append(stats);
            }
        }

        // if we can, retrieve the CNodeStateStats for each node.
        {
            TRY_LOCK(cs_main, lockMain);
            if (lockMain)
            {
                BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
                {
                    GetNodeStateStats(stats.nodestats.nodeid, stats.statestats);
                }
            }
        }

        if (sortColumn >= 0)
            // sort cacheNodeStats (use stable sort to prevent rows jumping around unneceesarily)
            qStableSort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));

        // build index map
        mapNodeRows.clear();
        int row = 0;
        BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
        {
            mapNodeRows.insert(std::pair<NodeId, int>(stats.nodestats.nodeid, row++));
        }
    }

    int size()
    {
        return cachedNodeStats.size();
    }

    CNodeCombinedStats *index(int idx)
    {
        if(idx >= 0 && idx < cachedNodeStats.size()) {
            return &cachedNodeStats[idx];
        }
        else
        {
            return 0;
        }
    }

};

PeerTableModel::PeerTableModel(ClientModel *parent) :
    QAbstractTableModel(parent),clientModel(parent),timer(0)
{
    columns << tr("Address") << tr("User Agent") << tr("Start Height");
    priv = new PeerTablePriv();
    // default to unsorted
    priv->sortColumn = -1;

    // set up timer for auto refresh
    timer = new QTimer();
    connect(timer, SIGNAL(timeout()), SLOT(refresh()));

    // load initial data
    refresh();
}

void PeerTableModel::startAutoRefresh(int msecs)
{
    timer->setInterval(1000);
    timer->start();
}

void PeerTableModel::stopAutoRefresh()
{
    timer->stop();
}

int PeerTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int PeerTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return 3;
}

QVariant PeerTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    CNodeCombinedStats *rec = static_cast<CNodeCombinedStats*>(index.internalPointer());

    if(role == Qt::DisplayRole)
    {
        switch(index.column())
        {
        case Address:
            return QVariant(rec->nodestats.addrName.c_str());
        case Subversion:
            return QVariant(rec->nodestats.cleanSubVer.c_str());
        case Height:
            return rec->nodestats.nStartingHeight;
        }
    }
    return QVariant();
}

QVariant PeerTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole && section < columns.size())
        {
            return columns[section];
        }
    }
    return QVariant();
}

Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return 0;

    Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    return retval;
}

QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    CNodeCombinedStats *data = priv->index(row);

    if (data)
    {
        return createIndex(row, column, data);
    }
    else
    {
        return QModelIndex();
    }
}

const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) {
    return priv->index(idx);
}

void PeerTableModel::refresh()
{
    emit layoutAboutToBeChanged();
    priv->refreshPeers();
    emit layoutChanged();
}

int PeerTableModel::getRowByNodeId(NodeId nodeid)
{
    std::map<NodeId, int>::iterator it = priv->mapNodeRows.find(nodeid);
    if (it == priv->mapNodeRows.end())
        return -1;

    return it->second;
}

void PeerTableModel::sort(int column, Qt::SortOrder order)
{
    priv->sortColumn = column;
    priv->sortOrder = order;
    refresh();
}
<|endoftext|>


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

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

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

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

#include "tensorflow/core/grappler/optimizers/implementation_selector.h"

#include <string>

#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/grappler/costs/graph_properties.h"
#include "tensorflow/core/grappler/grappler_item.h"
#include "tensorflow/core/grappler/op_types.h"
#include "tensorflow/core/grappler/optimizers/custom_graph_optimizer.h"
#include "tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.h"
#include "tensorflow/core/grappler/optimizers/function_api_info.h"
#include "tensorflow/core/grappler/utils/graph_view.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/stringpiece.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/util/device_name_utils.h"

namespace tensorflow {
namespace grappler {

// The overall idea for the function swap is like below:
//          -----------                            -----------
//  inp_1 ->|  P_C    | -> out_1         g_inp_1 ->|  P_C    | -> g_out_1
//  inp_2 ->| forward | -> out_2         g_inp_2 ->| backward| -> g_out_2
//          | FUNC_1  | -> out_3         g_inp_3 ->| FUNC_1  |
//          -----------                            -----------
//           |  |  |                                 ^  ^  ^
//           v  v  v                                 |  |  |
//           s1 s2 s3                                s1 s2 s3
//           |                                       ^
//           |                                       |
//           |             --------------            |
//           |-----------> | Identity_1 | ---------->|
//                         --------------
// P_C: op Partitioned_call or stateful_partitioned_call
// FUNC1 (forward): TF function generated for the forward path.
// FUNC1 (backward): TF function generated for the backward path.
// inp_x: input tensors for the forward path.
// out_x: output tensors for the forward path.
// g_inp_x: gradient input tensors for the backward path.
// g_out_x: gradient output tensors for the backward path.
// s_x: intermediate result generated by forward tf function, which will be
//      consumed by backward function for gradient calculation.
//
// In the example above, the FUNC_1 takes 2 inputs, and return 3 outputs, in the
// meantime, generate 3 intermediate results for gradient calculation.
// The backward function will take 6 inputs, 3 for the gradient value for out_x,
// and 3 for the intermediate results s1/2/3. It returns 2 outputs for gradient
// value wrt inp_x.
//
// Given the graph, especially after the device placement is done, we could
// check if there is an alternative FUNC_2 that is better for the assigned
// device type. Note that FUNC_2 (both forward and backward) should have same
// amount of input output tensor with same dtype. However, it can generate
// different intermediate state tensor, both number wise and type wise, since it
// depends on the implementation detail.
//
// Also note that there might be some Identity op being added to the output of
// the forward function by IsolatePlacerInspectionRequiredOps for device
// placement. When the output DTYPE changes when switching from FUNC_1 to
// FUNC_2, the Identity node down the stream also need to be updated with new
// DTYPE.
//
// Based on this, the rewrite need to happen for following items:
//
// 1. P_C forward/backward need to use FUNC_2 instead of FUNC_1.
// 2. The T_IN for P_C backward need to be updated since the s_x can be
//    different between FUNC_1 and FUNC_2.
// 3. The T_OUT for P_C forward need to be updated since the s_x can be
//    different between FUNC_1 and FUNC_2.
// 4. The input edge for P_C backward need to be updated since the amount of
//    intermediate result can be different between FUNC_1 and FUNC_2.
// 5. DTYPE of the Identity node after s_1/2/3 need to be updated if they exist.

string FindForwardNode(utils::MutableNodeView* backward_node) {
  // For the tf function, Identity op node might be added by
  // placer_inspection_required_ops_utils for device placement. Those ops might
  // be removed by model_pruner, or stay there if the Identity op is cross
  // device. Given the partitioned_call node for backward function, we want to
  // find the partitioned_call node for the forward function, so that we can
  // add/remove/updated input tensors for backward function, which is the step
  // 4 as described above.

  // Find the last input
  const int last_input_index = backward_node->NumRegularFanins() - 1;
  const utils::MutableFanoutView& input =
      backward_node->GetRegularFanin(last_input_index);
  // For the input node, it should either be the partitioned call, which is
  // the forward node we need, or a Identity op which just pass through the
  // output of the partitioned call.
  if (IsIdentity(*input.node_view()->node())) {
    // Find the only input to this op, which should be the original forward node
    return input.node_view()->node()->input(0);
  } else if (IsPartitionedCall(*input.node_view()->node()) ||
             IsStatefulPartitionedCall(*input.node_view()->node())) {
    // Found the forward node.
    return backward_node->node()->input(last_input_index);
  } else {
    // Unhandled situation.
    return "";
  }
}

void UpdateForwardIdentityNodeDtype(utils::MutableNodeView* forward_node,
                                    const DataTypeVector& dtypes) {
  const auto& fanouts_vector = forward_node->GetRegularFanouts();
  for (int pos = 0; pos < fanouts_vector.size(); ++pos) {
    const auto& fanouts_at_pos = fanouts_vector[pos];
    for (const auto& fanout : fanouts_at_pos) {
      if ("Identity" == fanout.node_view()->GetOp()) {
        (*fanout.node_view()->node()->mutable_attr())["T"].set_type(
            dtypes[pos]);
        VLOG(3) << "Updated DTYPE for Identity node: "
                << fanout.node_view()->node()->DebugString();
      }
    }
  }
}

Status UpdateNodeDef(utils::MutableNodeView* node_view, const string& funcName,
                     const FunctionApiInfo& apiInfo) {
  NodeDef* node_def = node_view->node();

  VLOG(3) << "Node def before swap is: " << node_def->DebugString();

  // For step 1 above.
  node_def->mutable_attr()->find("f")->second.mutable_func()->set_name(
      funcName);

  // For step 2 above.
  auto tin = node_def->mutable_attr()->find("Tin");
  tin->second.mutable_list()->clear_type();
  for (const auto& tin_dtype : apiInfo.input_arg_dtypes()) {
    tin->second.mutable_list()->add_type(tin_dtype);
  }

  // For step 3 above.
  auto tout = node_def->mutable_attr()->find("Tout");
  tout->second.mutable_list()->clear_type();
  for (const auto& tout_dtype : apiInfo.output_arg_dtypes()) {
    tout->second.mutable_list()->add_type(tout_dtype);
  }

  if (apiInfo.function_type() == FunctionApiInfo::BACKWARD) {
    // For step 4 above.
    const int prev_input_size = node_def->input_size();
    const int diff = prev_input_size - apiInfo.input_arg_dtypes().size();
    if (diff >= 0) {
      for (int i = 0; i < diff; ++i) node_def->mutable_input()->RemoveLast();
    } else {
      // Adding new inputs for internal states, the name of the internal states
      // should be in format "{forward_node_name}:{index}", where the newly
      // added index should start from last index of the state.
      // Eg:
      // {
      //   input: "gradients/unified_lstm/strided_slice_1_grad/StridedSliceGrad"
      //   input: "gradients/zeros_like_1"
      //   input: "gradients/zeros_like_2"
      //   input: "unified_lstm/StatefulPartitionedCall:3"
      //   input: "unified_lstm/StatefulPartitionedCall:4"
      //   # New input should be "unified_lstm/StatefulPartitionedCall:5"
      // }
      const string last_input = FindForwardNode(node_view);
      const std::vector<string> name_index = ::absl::StrSplit(last_input, ':');
      if (name_index.size() != 2) {
        return errors::InvalidArgument(
            "Invalid format of input node name: ", last_input,
            " Expected: {forward_node_name}:{index}");
      }
      const absl::string_view node_name = name_index[0];
      int last_index;
      if (!::absl::SimpleAtoi(name_index[1], &last_index)) {
        return errors::InvalidArgument(
            "The index of input node is expected to be number, got: ",
            name_index[1]);
      }
      for (int i = 1; i <= -diff; ++i)
        node_def->add_input(strings::StrCat(node_name, ":", i + last_index));
    }
  } else if (apiInfo.function_type() == FunctionApiInfo::FORWARD) {
    // For forward function, since the DTYPE of the intermediate state might
    // have been changed, we want to update the down stream Identity node if
    // any. This is the step 5 in the commend above.
    UpdateForwardIdentityNodeDtype(node_view, apiInfo.output_arg_dtypes());
  }

  VLOG(3) << "Node def after swap is: " << node_def->DebugString();
  return Status::OK();
}

Status ImplementationSelector::LoadFunctions(const GraphDef& graph) {
  lib_info_ = absl::make_unique<FunctionLibraryApiInfo>();
  TF_RETURN_IF_ERROR(lib_info_->Init(graph.library()));
  return Status::OK();
}

Status ImplementationSelector::MaybeOptimizeFunctionCall(
    utils::MutableNodeView* node_view) const {
  // There are two ways of calling functions:
  //  1. By specifying an op name as a function name, or
  //  2. Via the @defun functional interface, where the real function call
  //     happens with partitionedcall op, and the function name appear as the
  //     attribute with name "f" and type func. In this use case, there are more
  //     attributes need to be taken care, like Tin and Tout which take care of
  //     the DTYPE of input/output.
  NodeDef* node_def = node_view->node();

  std::vector<string> function_attribute_names;
  for (const auto& attr : node_def->attr()) {
    if (attr.second.has_func() &&
        lib_info_->GetApiInfo(attr.second.func().name()) != nullptr) {
      function_attribute_names.emplace_back(attr.first);
    }
  }

  if (function_attribute_names.empty() &&
      lib_info_->GetApiInfo(node_def->op()) == nullptr) {
    // A regular op, or a function which has no interface.
    return Status::OK();
  }

  DeviceNameUtils::ParsedName parsed_name;
  if (!DeviceNameUtils::ParseFullName(node_def->device(), &parsed_name) ||
      !parsed_name.has_type) {
    return errors::Internal("Could not parse device name:", node_def->device());
  }
  VLOG(2) << "Op " << node_def->name() << " runs on " << node_def->device()
          << " = (" << parsed_name.type << ")";

  for (const auto& attr_name : function_attribute_names) {
    string function_name = node_def->attr().at(attr_name).func().name();
    // Skip the function if its already optimized by function optimizer.
    if (::absl::StrContains(function_name, "_specialized_for_")) continue;
    std::vector<string> equiv_func_names;
    TF_RETURN_IF_ERROR(lib_info_->GetEquivalentImplementations(
        function_name, &equiv_func_names));
    for (const auto& func_name : equiv_func_names) {
      const auto& func_api_info = lib_info_->GetApiInfo(func_name);
      if (func_api_info->preferred_device() == parsed_name.type) {
        VLOG(2) << "Swapping: " << function_name << " TO: " << func_name;
        TF_RETURN_IF_ERROR(UpdateNodeDef(node_view, func_name, *func_api_info));
        break;
      }
    }
  }

  if (lib_info_->GetApiInfo(node_def->op()) != nullptr &&
      !::absl::StrContains(node_def->op(), "_specialized_for_")) {
    std::vector<string> equiv_func_names;
    TF_RETURN_IF_ERROR(lib_info_->GetEquivalentImplementations(
        node_def->op(), &equiv_func_names));
    for (const string& func_name : equiv_func_names) {
      const auto func_api_info = lib_info_->GetApiInfo(func_name);
      if (func_api_info->preferred_device() == parsed_name.type) {
        node_def->set_op(func_name);
        break;
      }
    }
  }
  return Status::OK();
}

Status ImplementationSelector::SelectImplementation(GraphDef* graph) const {
  if (!graph->has_library()) {
    VLOG(2) << "Skipping graph since it does not have function def";
    return Status::OK();
  }
  if (lib_info_->empty()) {
    VLOG(2) << "Skipping optimization since lib_info is empty";
    return Status::OK();
  }

  Status status;
  utils::MutableGraphView graph_view(graph, &status);
  TF_RETURN_IF_ERROR(status);

  const int num_nodes = graph_view.NumNodes();
  for (int k = 0; k < num_nodes; ++k)
    TF_RETURN_IF_ERROR(MaybeOptimizeFunctionCall(graph_view.GetNode(k)));

  return Status::OK();
}

Status ImplementationSelector::Optimize(Cluster* cluster,
                                        const GrapplerItem& item,
                                        GraphDef* optimized_graph) {
  auto status = LoadFunctions(item.graph);
  // Eat up the error from function loading, since this optimizer might run
  // several times, and might try to run against functions generated by
  // function_optimizer from previous runs, which will fail due to function
  // signature mismatch.
  if (!status.ok()) {
    LOG(WARNING) << "Skipping optimization due to error while loading function "
                 << "libraries: " << status;
    return errors::Aborted("Skipped Optimization");
  }
  *optimized_graph = item.graph;
  return SelectImplementation(optimized_graph);
}

}  // end namespace grappler
}  // end namespace tensorflow
<commit_msg>Update the warning message in implementation_selector to VLOG.<commit_after>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

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

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

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

#include "tensorflow/core/grappler/optimizers/implementation_selector.h"

#include <string>

#include "absl/strings/numbers.h"
#include "absl/strings/str_split.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/grappler/costs/graph_properties.h"
#include "tensorflow/core/grappler/grappler_item.h"
#include "tensorflow/core/grappler/op_types.h"
#include "tensorflow/core/grappler/optimizers/custom_graph_optimizer.h"
#include "tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.h"
#include "tensorflow/core/grappler/optimizers/function_api_info.h"
#include "tensorflow/core/grappler/utils/graph_view.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/stringpiece.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/util/device_name_utils.h"

namespace tensorflow {
namespace grappler {

// The overall idea for the function swap is like below:
//          -----------                            -----------
//  inp_1 ->|  P_C    | -> out_1         g_inp_1 ->|  P_C    | -> g_out_1
//  inp_2 ->| forward | -> out_2         g_inp_2 ->| backward| -> g_out_2
//          | FUNC_1  | -> out_3         g_inp_3 ->| FUNC_1  |
//          -----------                            -----------
//           |  |  |                                 ^  ^  ^
//           v  v  v                                 |  |  |
//           s1 s2 s3                                s1 s2 s3
//           |                                       ^
//           |                                       |
//           |             --------------            |
//           |-----------> | Identity_1 | ---------->|
//                         --------------
// P_C: op Partitioned_call or stateful_partitioned_call
// FUNC1 (forward): TF function generated for the forward path.
// FUNC1 (backward): TF function generated for the backward path.
// inp_x: input tensors for the forward path.
// out_x: output tensors for the forward path.
// g_inp_x: gradient input tensors for the backward path.
// g_out_x: gradient output tensors for the backward path.
// s_x: intermediate result generated by forward tf function, which will be
//      consumed by backward function for gradient calculation.
//
// In the example above, the FUNC_1 takes 2 inputs, and return 3 outputs, in the
// meantime, generate 3 intermediate results for gradient calculation.
// The backward function will take 6 inputs, 3 for the gradient value for out_x,
// and 3 for the intermediate results s1/2/3. It returns 2 outputs for gradient
// value wrt inp_x.
//
// Given the graph, especially after the device placement is done, we could
// check if there is an alternative FUNC_2 that is better for the assigned
// device type. Note that FUNC_2 (both forward and backward) should have same
// amount of input output tensor with same dtype. However, it can generate
// different intermediate state tensor, both number wise and type wise, since it
// depends on the implementation detail.
//
// Also note that there might be some Identity op being added to the output of
// the forward function by IsolatePlacerInspectionRequiredOps for device
// placement. When the output DTYPE changes when switching from FUNC_1 to
// FUNC_2, the Identity node down the stream also need to be updated with new
// DTYPE.
//
// Based on this, the rewrite need to happen for following items:
//
// 1. P_C forward/backward need to use FUNC_2 instead of FUNC_1.
// 2. The T_IN for P_C backward need to be updated since the s_x can be
//    different between FUNC_1 and FUNC_2.
// 3. The T_OUT for P_C forward need to be updated since the s_x can be
//    different between FUNC_1 and FUNC_2.
// 4. The input edge for P_C backward need to be updated since the amount of
//    intermediate result can be different between FUNC_1 and FUNC_2.
// 5. DTYPE of the Identity node after s_1/2/3 need to be updated if they exist.

string FindForwardNode(utils::MutableNodeView* backward_node) {
  // For the tf function, Identity op node might be added by
  // placer_inspection_required_ops_utils for device placement. Those ops might
  // be removed by model_pruner, or stay there if the Identity op is cross
  // device. Given the partitioned_call node for backward function, we want to
  // find the partitioned_call node for the forward function, so that we can
  // add/remove/updated input tensors for backward function, which is the step
  // 4 as described above.

  // Find the last input
  const int last_input_index = backward_node->NumRegularFanins() - 1;
  const utils::MutableFanoutView& input =
      backward_node->GetRegularFanin(last_input_index);
  // For the input node, it should either be the partitioned call, which is
  // the forward node we need, or a Identity op which just pass through the
  // output of the partitioned call.
  if (IsIdentity(*input.node_view()->node())) {
    // Find the only input to this op, which should be the original forward node
    return input.node_view()->node()->input(0);
  } else if (IsPartitionedCall(*input.node_view()->node()) ||
             IsStatefulPartitionedCall(*input.node_view()->node())) {
    // Found the forward node.
    return backward_node->node()->input(last_input_index);
  } else {
    // Unhandled situation.
    return "";
  }
}

void UpdateForwardIdentityNodeDtype(utils::MutableNodeView* forward_node,
                                    const DataTypeVector& dtypes) {
  const auto& fanouts_vector = forward_node->GetRegularFanouts();
  for (int pos = 0; pos < fanouts_vector.size(); ++pos) {
    const auto& fanouts_at_pos = fanouts_vector[pos];
    for (const auto& fanout : fanouts_at_pos) {
      if ("Identity" == fanout.node_view()->GetOp()) {
        (*fanout.node_view()->node()->mutable_attr())["T"].set_type(
            dtypes[pos]);
        VLOG(3) << "Updated DTYPE for Identity node: "
                << fanout.node_view()->node()->DebugString();
      }
    }
  }
}

Status UpdateNodeDef(utils::MutableNodeView* node_view, const string& funcName,
                     const FunctionApiInfo& apiInfo) {
  NodeDef* node_def = node_view->node();

  VLOG(3) << "Node def before swap is: " << node_def->DebugString();

  // For step 1 above.
  node_def->mutable_attr()->find("f")->second.mutable_func()->set_name(
      funcName);

  // For step 2 above.
  auto tin = node_def->mutable_attr()->find("Tin");
  tin->second.mutable_list()->clear_type();
  for (const auto& tin_dtype : apiInfo.input_arg_dtypes()) {
    tin->second.mutable_list()->add_type(tin_dtype);
  }

  // For step 3 above.
  auto tout = node_def->mutable_attr()->find("Tout");
  tout->second.mutable_list()->clear_type();
  for (const auto& tout_dtype : apiInfo.output_arg_dtypes()) {
    tout->second.mutable_list()->add_type(tout_dtype);
  }

  if (apiInfo.function_type() == FunctionApiInfo::BACKWARD) {
    // For step 4 above.
    const int prev_input_size = node_def->input_size();
    const int diff = prev_input_size - apiInfo.input_arg_dtypes().size();
    if (diff >= 0) {
      for (int i = 0; i < diff; ++i) node_def->mutable_input()->RemoveLast();
    } else {
      // Adding new inputs for internal states, the name of the internal states
      // should be in format "{forward_node_name}:{index}", where the newly
      // added index should start from last index of the state.
      // Eg:
      // {
      //   input: "gradients/unified_lstm/strided_slice_1_grad/StridedSliceGrad"
      //   input: "gradients/zeros_like_1"
      //   input: "gradients/zeros_like_2"
      //   input: "unified_lstm/StatefulPartitionedCall:3"
      //   input: "unified_lstm/StatefulPartitionedCall:4"
      //   # New input should be "unified_lstm/StatefulPartitionedCall:5"
      // }
      const string last_input = FindForwardNode(node_view);
      const std::vector<string> name_index = ::absl::StrSplit(last_input, ':');
      if (name_index.size() != 2) {
        return errors::InvalidArgument(
            "Invalid format of input node name: ", last_input,
            " Expected: {forward_node_name}:{index}");
      }
      const absl::string_view node_name = name_index[0];
      int last_index;
      if (!::absl::SimpleAtoi(name_index[1], &last_index)) {
        return errors::InvalidArgument(
            "The index of input node is expected to be number, got: ",
            name_index[1]);
      }
      for (int i = 1; i <= -diff; ++i)
        node_def->add_input(strings::StrCat(node_name, ":", i + last_index));
    }
  } else if (apiInfo.function_type() == FunctionApiInfo::FORWARD) {
    // For forward function, since the DTYPE of the intermediate state might
    // have been changed, we want to update the down stream Identity node if
    // any. This is the step 5 in the commend above.
    UpdateForwardIdentityNodeDtype(node_view, apiInfo.output_arg_dtypes());
  }

  VLOG(3) << "Node def after swap is: " << node_def->DebugString();
  return Status::OK();
}

Status ImplementationSelector::LoadFunctions(const GraphDef& graph) {
  lib_info_ = absl::make_unique<FunctionLibraryApiInfo>();
  TF_RETURN_IF_ERROR(lib_info_->Init(graph.library()));
  return Status::OK();
}

Status ImplementationSelector::MaybeOptimizeFunctionCall(
    utils::MutableNodeView* node_view) const {
  // There are two ways of calling functions:
  //  1. By specifying an op name as a function name, or
  //  2. Via the @defun functional interface, where the real function call
  //     happens with partitionedcall op, and the function name appear as the
  //     attribute with name "f" and type func. In this use case, there are more
  //     attributes need to be taken care, like Tin and Tout which take care of
  //     the DTYPE of input/output.
  NodeDef* node_def = node_view->node();

  std::vector<string> function_attribute_names;
  for (const auto& attr : node_def->attr()) {
    if (attr.second.has_func() &&
        lib_info_->GetApiInfo(attr.second.func().name()) != nullptr) {
      function_attribute_names.emplace_back(attr.first);
    }
  }

  if (function_attribute_names.empty() &&
      lib_info_->GetApiInfo(node_def->op()) == nullptr) {
    // A regular op, or a function which has no interface.
    return Status::OK();
  }

  DeviceNameUtils::ParsedName parsed_name;
  if (!DeviceNameUtils::ParseFullName(node_def->device(), &parsed_name) ||
      !parsed_name.has_type) {
    return errors::Internal("Could not parse device name:", node_def->device());
  }
  VLOG(2) << "Op " << node_def->name() << " runs on " << node_def->device()
          << " = (" << parsed_name.type << ")";

  for (const auto& attr_name : function_attribute_names) {
    string function_name = node_def->attr().at(attr_name).func().name();
    // Skip the function if its already optimized by function optimizer.
    if (::absl::StrContains(function_name, "_specialized_for_")) continue;
    std::vector<string> equiv_func_names;
    TF_RETURN_IF_ERROR(lib_info_->GetEquivalentImplementations(
        function_name, &equiv_func_names));
    for (const auto& func_name : equiv_func_names) {
      const auto& func_api_info = lib_info_->GetApiInfo(func_name);
      if (func_api_info->preferred_device() == parsed_name.type) {
        VLOG(2) << "Swapping: " << function_name << " TO: " << func_name;
        TF_RETURN_IF_ERROR(UpdateNodeDef(node_view, func_name, *func_api_info));
        break;
      }
    }
  }

  if (lib_info_->GetApiInfo(node_def->op()) != nullptr &&
      !::absl::StrContains(node_def->op(), "_specialized_for_")) {
    std::vector<string> equiv_func_names;
    TF_RETURN_IF_ERROR(lib_info_->GetEquivalentImplementations(
        node_def->op(), &equiv_func_names));
    for (const string& func_name : equiv_func_names) {
      const auto func_api_info = lib_info_->GetApiInfo(func_name);
      if (func_api_info->preferred_device() == parsed_name.type) {
        node_def->set_op(func_name);
        break;
      }
    }
  }
  return Status::OK();
}

Status ImplementationSelector::SelectImplementation(GraphDef* graph) const {
  if (!graph->has_library()) {
    VLOG(2) << "Skipping graph since it does not have function def";
    return Status::OK();
  }
  if (lib_info_->empty()) {
    VLOG(2) << "Skipping optimization since lib_info is empty";
    return Status::OK();
  }

  Status status;
  utils::MutableGraphView graph_view(graph, &status);
  TF_RETURN_IF_ERROR(status);

  const int num_nodes = graph_view.NumNodes();
  for (int k = 0; k < num_nodes; ++k)
    TF_RETURN_IF_ERROR(MaybeOptimizeFunctionCall(graph_view.GetNode(k)));

  return Status::OK();
}

Status ImplementationSelector::Optimize(Cluster* cluster,
                                        const GrapplerItem& item,
                                        GraphDef* optimized_graph) {
  auto status = LoadFunctions(item.graph);
  // Eat up the error from function loading, since this optimizer might run
  // several times, and might try to run against functions generated by
  // function_optimizer from previous runs, which will fail due to function
  // signature mismatch.
  if (!status.ok()) {
    VLOG(2) << "Skipping optimization due to error while loading function "
            << "libraries: " << status;
    return errors::Aborted("Skipped Optimization");
  }
  *optimized_graph = item.graph;
  return SelectImplementation(optimized_graph);
}

}  // end namespace grappler
}  // end namespace tensorflow
<|endoftext|>


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

#include "modules/perception/obstacle/camera/lane_post_process/cc_lane_post_processor/cc_lane_post_processor.h"

#include <memory>
#include <string>
#include <vector>

#include "gtest/gtest.h"
#include "opencv2/opencv.hpp"

#include "modules/common/log.h"
#include "modules/perception/common/perception_gflags.h"
#include "modules/perception/lib/config_manager/config_manager.h"

using std::shared_ptr;
using std::string;
using std::vector;

namespace apollo {
namespace perception {

DEFINE_string(test_dir,
              "/apollo/modules/perception/data/cc_lane_post_processor_test/",
              "test data directory");

static const cv::Scalar lane_mask_color(0, 255, 255);  // yellow

template<typename T = float>
T GetPolyValue(T a, T b, T c, T d, T x) {
  T y = d;
  T v = x;
  y += (c * v);
  v *= x;
  y += (b * v);
  v *= x;
  y += (a * v);
  return y;
}

class CCLanePostProcessorTest : public ::testing::Test {
 protected:
  CCLanePostProcessorTest() {}
  ~CCLanePostProcessorTest() {}
  void SetUp() override {
    cc_lane_post_processor_.reset(new CCLanePostProcessor());
  }

 protected:
  shared_ptr<CCLanePostProcessor> cc_lane_post_processor_;
};

TEST_F(CCLanePostProcessorTest, test_lane_post_process_vis) {
  FLAGS_work_root = "modules/perception";
  FLAGS_config_manager_path = "conf/config_manager.config";
  CHECK(ConfigManager::instance()->Init()) << "failed to Init ConfigManager";

  cc_lane_post_processor_->set_vis(true);
  EXPECT_TRUE(cc_lane_post_processor_->Init());

  const string input_test_image_file = FLAGS_test_dir + "test_image.jpg";
  AINFO << "test original image file: " << input_test_image_file;
  cv::Mat test_image_ori =
      cv::imread(input_test_image_file, CV_LOAD_IMAGE_COLOR);

  const string input_lane_map_file = FLAGS_test_dir + "lane_map.jpg";
  AINFO << "input lane map file: " << input_lane_map_file;
  cv::Mat lane_map_ori =
      cv::imread(input_lane_map_file, CV_LOAD_IMAGE_GRAYSCALE);

  string input_lane_map_file_test = FLAGS_test_dir + "test_lane_map.jpg";
  cv::imwrite(input_lane_map_file_test, lane_map_ori);

  cv::Mat lane_map;
  lane_map_ori.convertTo(lane_map, CV_32FC1, 1.0f / 255.0f, 0.0f);

  ConfigManager *config_manager = ConfigManager::instance();
  const ModelConfig *model_config
      = config_manager->GetModelConfig(cc_lane_post_processor_->name());
  //<< "not found model: " << cc_lane_post_processor_->name();

  float lane_map_conf_thresh = 0.5f;
  CHECK(model_config->GetValue("lane_map_confidence_thresh",
                               &lane_map_conf_thresh))
  << "the confidence threshold of label map not found.";

  LaneObjectsPtr lane_objects = std::make_shared<LaneObjects>();
  CameraLanePostProcessOptions lane_post_process_options;
  cc_lane_post_processor_->Process(
    lane_map, lane_post_process_options, &lane_objects);

  AINFO << "#lane objects = " << lane_objects->size();

  cv::Mat vis_lane_objects_image;
  test_image_ori.copyTo(vis_lane_objects_image);
  for (int h = 0; h < lane_map.rows; ++h) {
    for (int w = 0; w < lane_map.cols; ++w) {
      if (lane_map.at<float>(h, w) >= lane_map_conf_thresh) {
        vis_lane_objects_image.at<cv::Vec3b>(h, w)[0] =
            static_cast<unsigned char>(lane_mask_color[0]);
        vis_lane_objects_image.at<cv::Vec3b>(h, w)[1] =
            static_cast<unsigned char>(lane_mask_color[1]);
        vis_lane_objects_image.at<cv::Vec3b>(h, w)[2] =
            static_cast<unsigned char>(lane_mask_color[2]);
      }
    }
  }

  // draw lane objects
  for (size_t k = 0; k < lane_objects->size(); ++k) {
    if (lane_objects->at(k).is_compensated) {
      continue;
    }

    cv::Scalar lane_object_color;
    switch (lane_objects->at(k).spatial) {
      case SpatialLabelType::L_0: {
        lane_object_color = cv::Scalar(0, 0, 255);  // red
        break;
      }
      case SpatialLabelType::L_1: {
        lane_object_color = cv::Scalar(255, 0, 255);  // magenta
        break;
      }
      case SpatialLabelType::L_2: {
        lane_object_color = cv::Scalar(255, 63, 153);  // purple
        break;
      }
      case SpatialLabelType::R_0: {
        lane_object_color = cv::Scalar(255, 0, 0);  // blue
        break;
      }
      case SpatialLabelType::R_1: {
        lane_object_color = cv::Scalar(255, 255, 0);  // cyan
        break;
      }
      case SpatialLabelType::R_2: {
        lane_object_color = cv::Scalar(63, 255, 192);  // greenyellow
        break;
      }
      default: {
        AERROR << "unknown lane spatial label: "
               << static_cast<int>(lane_objects->at(k).spatial);
      }
    }

    // Besides the fitted polynomial curves we draw lane markers as well
    for (auto p = lane_objects->at(k).image_pos.begin();
         p != lane_objects->at(k).image_pos.end(); ++p) {
      cv::circle(vis_lane_objects_image, cv::Point(static_cast<int>(p->x()),
                                                   static_cast<int>(p->y())),
                                                   4, lane_object_color, -1);
    }

    // draw polynomial curve
    float img_y_start =
        static_cast<float>(lane_objects->at(k).img_curve.x_start);
    float img_y_end =
        static_cast<float>(lane_objects->at(k).img_curve.x_end);
    float start = std::min(img_y_start, img_y_end);
    float end = std::max(img_y_start, img_y_end);
    float a = lane_objects->at(k).img_curve.a;
    float b = lane_objects->at(k).img_curve.b;
    float c = lane_objects->at(k).img_curve.c;
    float d = lane_objects->at(k).img_curve.d;

    for (float l = start; l <= end; l++) {
      cv::circle(vis_lane_objects_image,
                 cv::Point(static_cast<int>(GetPolyValue(a, b, c, d, l)),
                           static_cast<int>(l)),
                 2, lane_object_color, -1);
    }
  }

  string lane_objects_image_file =
      FLAGS_test_dir + "test_lane_objects_image.jpg";
  cv::imwrite(lane_objects_image_file, vis_lane_objects_image);
}

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

#include "modules/perception/obstacle/camera/lane_post_process/cc_lane_post_processor/cc_lane_post_processor.h"

#include <memory>
#include <string>
#include <vector>

#include "gtest/gtest.h"
#include "opencv2/opencv.hpp"

#include "modules/common/log.h"
#include "modules/perception/common/perception_gflags.h"
#include "modules/perception/lib/config_manager/config_manager.h"

using std::shared_ptr;
using std::string;
using std::vector;

namespace apollo {
namespace perception {

DEFINE_string(test_dir,
              "/apollo/modules/perception/data/cc_lane_post_processor_test/",
              "test data directory");

static const cv::Scalar lane_mask_color(0, 255, 255);  // yellow

template<typename T = float>
T GetPolyValue(T a, T b, T c, T d, T x) {
  T y = d;
  T v = x;
  y += (c * v);
  v *= x;
  y += (b * v);
  v *= x;
  y += (a * v);
  return y;
}

class CCLanePostProcessorTest : public ::testing::Test {
 protected:
  CCLanePostProcessorTest() {}
  ~CCLanePostProcessorTest() {}
  void SetUp() override {
    cc_lane_post_processor_.reset(new CCLanePostProcessor());
  }

 protected:
  shared_ptr<CCLanePostProcessor> cc_lane_post_processor_;
};

TEST_F(CCLanePostProcessorTest, test_lane_post_process_vis) {
  FLAGS_work_root = "modules/perception";
  FLAGS_config_manager_path = "conf/config_manager.config";
  CHECK(ConfigManager::instance()->Init()) << "failed to Init ConfigManager";

  cc_lane_post_processor_->set_vis(true);
  EXPECT_TRUE(cc_lane_post_processor_->Init());

  const string input_test_image_file = FLAGS_test_dir + "test_image.jpg";
  AINFO << "test original image file: " << input_test_image_file;
  cv::Mat test_image_ori =
      cv::imread(input_test_image_file, CV_LOAD_IMAGE_COLOR);

  const string input_lane_map_file = FLAGS_test_dir + "lane_map.jpg";
  AINFO << "input lane map file: " << input_lane_map_file;
  cv::Mat lane_map_ori =
      cv::imread(input_lane_map_file, CV_LOAD_IMAGE_GRAYSCALE);

  string input_lane_map_file_test = FLAGS_test_dir + "test_lane_map.jpg";
  cv::imwrite(input_lane_map_file_test, lane_map_ori);

  cv::Mat lane_map;
  lane_map_ori.convertTo(lane_map, CV_32FC1, 1.0f / 255.0f, 0.0f);

  ConfigManager *config_manager = ConfigManager::instance();
  const ModelConfig *model_config
      = config_manager->GetModelConfig(cc_lane_post_processor_->name());

  float lane_map_conf_thresh = 0.5f;
  CHECK(model_config->GetValue("lane_map_confidence_thresh",
                               &lane_map_conf_thresh))
  << "the confidence threshold of label map not found.";
  AINFO << "lane_map_conf_thresh = " << lane_map_conf_thresh;

  LaneObjectsPtr lane_objects = std::make_shared<LaneObjects>();
  CameraLanePostProcessOptions lane_post_process_options;
  cc_lane_post_processor_->Process(
    lane_map, lane_post_process_options, &lane_objects);

  AINFO << "#lane objects = " << lane_objects->size();

  cv::Mat vis_lane_objects_image;
  test_image_ori.copyTo(vis_lane_objects_image);
  for (int h = 0; h < lane_map.rows; ++h) {
    for (int w = 0; w < lane_map.cols; ++w) {
      if (lane_map.at<float>(h, w) >= lane_map_conf_thresh) {
        vis_lane_objects_image.at<cv::Vec3b>(h, w)[0] =
            static_cast<unsigned char>(lane_mask_color[0]);
        vis_lane_objects_image.at<cv::Vec3b>(h, w)[1] =
            static_cast<unsigned char>(lane_mask_color[1]);
        vis_lane_objects_image.at<cv::Vec3b>(h, w)[2] =
            static_cast<unsigned char>(lane_mask_color[2]);
      }
    }
  }

  // draw lane objects
  for (size_t k = 0; k < lane_objects->size(); ++k) {
    if (lane_objects->at(k).is_compensated) {
      continue;
    }

    cv::Scalar lane_object_color;
    switch (lane_objects->at(k).spatial) {
      case SpatialLabelType::L_0: {
        lane_object_color = cv::Scalar(0, 0, 255);  // red
        break;
      }
      case SpatialLabelType::L_1: {
        lane_object_color = cv::Scalar(255, 0, 255);  // magenta
        break;
      }
      case SpatialLabelType::L_2: {
        lane_object_color = cv::Scalar(255, 63, 153);  // purple
        break;
      }
      case SpatialLabelType::R_0: {
        lane_object_color = cv::Scalar(255, 0, 0);  // blue
        break;
      }
      case SpatialLabelType::R_1: {
        lane_object_color = cv::Scalar(255, 255, 0);  // cyan
        break;
      }
      case SpatialLabelType::R_2: {
        lane_object_color = cv::Scalar(63, 255, 192);  // greenyellow
        break;
      }
      default: {
        AERROR << "unknown lane spatial label: "
               << static_cast<int>(lane_objects->at(k).spatial);
      }
    }

    // Besides the fitted polynomial curves we draw lane markers as well
    for (auto p = lane_objects->at(k).image_pos.begin();
         p != lane_objects->at(k).image_pos.end(); ++p) {
      cv::circle(vis_lane_objects_image, cv::Point(static_cast<int>(p->x()),
                                                   static_cast<int>(p->y())),
                                                   4, lane_object_color, -1);
    }

    // draw polynomial curve
    float img_y_start =
        static_cast<float>(lane_objects->at(k).img_curve.x_start);
    float img_y_end =
        static_cast<float>(lane_objects->at(k).img_curve.x_end);
    float start = std::min(img_y_start, img_y_end);
    float end = std::max(img_y_start, img_y_end);
    float a = lane_objects->at(k).img_curve.a;
    float b = lane_objects->at(k).img_curve.b;
    float c = lane_objects->at(k).img_curve.c;
    float d = lane_objects->at(k).img_curve.d;

    for (float l = start; l <= end; l++) {
      cv::circle(vis_lane_objects_image,
                 cv::Point(static_cast<int>(GetPolyValue(a, b, c, d, l)),
                           static_cast<int>(l)),
                 2, lane_object_color, -1);
    }
  }

  string lane_objects_image_file =
      FLAGS_test_dir + "test_lane_objects_image.jpg";
  cv::imwrite(lane_objects_image_file, vis_lane_objects_image);
}

}  // namespace perception
}  // namespace apollo
<|endoftext|>