<commit_before>/*
 * opencog/dynamics/attention/SimpleImportanceDiffusionAgent.cc
 *
 * Copyright (C) 2014 Cosmo Harrigan
 * All Rights Reserved
 *
 * Written by Cosmo Harrigan
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "SimpleImportanceDiffusionAgent.h"
#include "SpreadDecider.h"
#include <time.h>
#include <math.h>
#include <opencog/atomspace/Link.h>
#include <opencog/dynamics/attention/atom_types.h>
#include <opencog/server/CogServer.h>
#include <opencog/util/Config.h>
#include <opencog/util/platform.h>
#include <opencog/util/mt19937ar.h>
#include <opencog/util/algorithm.h>

#define DEBUG
namespace opencog
{

SimpleImportanceDiffusionAgent::SimpleImportanceDiffusionAgent(CogServer& cs) :
    Agent(cs)
{
    static const std::string defaultConfig[] = {
        //! Maximum percentage of STI that is spread from an atom
        "ECAN_MAX_SPREAD_PERCENTAGE","0.5",
        "ECAN_SPREAD_HEBBIAN_ONLY","false",
        "",""
    };
    setParameters(defaultConfig);
    spreadDecider = NULL;

    //! @todo won't respond to the parameters being changed later
    //! (not a problem at present, but could get awkward with, for example,
    //! automatic parameter adaptation)
    maxSpreadPercentage = (float) (config().get_double("ECAN_MAX_SPREAD_PERCENTAGE"));

    setSpreadDecider(STEP);

    spreadHebbianOnly = config().get_bool("ECAN_SPREAD_HEBBIAN_ONLY");

    // Provide a logger
    log = NULL;
    setLogger(new opencog::Logger("SimpleImportanceDiffusionAgent.log", Logger::FINE, true));
}

void SimpleImportanceDiffusionAgent::setLogger(Logger* _log)
{
    if (log) delete log;
    log = _log;
}

Logger* SimpleImportanceDiffusionAgent::getLogger()
{
    return log;
}

void SimpleImportanceDiffusionAgent::setSpreadDecider(int type, float shape)
{
    if (spreadDecider) {
        delete spreadDecider;
        spreadDecider = NULL;
    }
    switch (type) {
    case HYPERBOLIC:
        spreadDecider = (SpreadDecider*) new HyperbolicDecider(_cogserver, shape);
        break;
    case STEP:
        spreadDecider = (SpreadDecider*) new StepDecider(_cogserver);
        break;
    }

}

SimpleImportanceDiffusionAgent::~SimpleImportanceDiffusionAgent()
{
    if (spreadDecider) {
        delete spreadDecider;
        spreadDecider = NULL;
    }
}

void SimpleImportanceDiffusionAgent::run()
{
    as = &_cogserver.getAtomSpace();
    spreadDecider->setFocusBoundary(0);
    spreadImportance();
}

void SimpleImportanceDiffusionAgent::spreadImportance()
{
    HandleSeq diffusionSourceVector =
            SimpleImportanceDiffusionAgent::diffusionSourceVector();

    foreach (Handle atomSource, diffusionSourceVector)
    {
        // Check the decision function to determine if spreading will occur
        if (spreadDecider->spreadDecision(as->getSTI(atomSource))) {
            diffuseAtom(atomSource);
        }
    }
}

/*
 * Diffuses importance from one atom to its non-hebbian incident atoms
 * and hebbian adjacent atoms
 */
void SimpleImportanceDiffusionAgent::diffuseAtom(Handle atomSource)
{
    // TODO: What if the STI values of the atoms change during these updates?
    // This could go wrong if there were simultaneous updates in other threads.

    HandleSeq targetAtoms =
            SimpleImportanceDiffusionAgent::incidentAtoms(atomSource);

    HandleSeq hebbianAdjacentAtoms =
            SimpleImportanceDiffusionAgent::hebbianAdjacentAtoms(atomSource);

    targetAtoms.insert(targetAtoms.end(),
                       hebbianAdjacentAtoms.begin(),
                       hebbianAdjacentAtoms.end());

    std::map<Handle, double> probabilityVector =
            SimpleImportanceDiffusionAgent::probabilityVector(targetAtoms);

    AttentionValue::sti_t diffusionAmount =
            SimpleImportanceDiffusionAgent::calculateDiffusionAmount(atomSource);

    if (diffusionAmount == 0)
    {
        return;
    }

    // For each atomTarget in incident atoms
    foreach (Handle atomTarget, targetAtoms)
    {
        diffusionAmount = diffusionAmount * probabilityVector[atomTarget];

        // Trade STI between the source and target atoms
        as->setSTI(atomSource, as->getSTI(atomSource) - diffusionAmount);
        as->setSTI(atomTarget, as->getSTI(atomTarget) + diffusionAmount);

        // TODO: How to make this a transaction? This could go wrong if there
        // were simultaneous updates in other threads.
    }
}

/*
 * Returns a vector of atom handles that will diffuse STI
 *
 * Calculated as all atoms in the attentional focus (nodes and links)
 * excluding any hebbian links
 */
HandleSeq SimpleImportanceDiffusionAgent::diffusionSourceVector()
{

}

/*
 * Returns a vector of atom handles that are incident to a given atom
 *
 * Calculated as the set union of an atom's incoming and outgoing set,
 * excluding hebbian links
 */
HandleSeq SimpleImportanceDiffusionAgent::incidentAtoms(Handle h)
{

}

/*
 * Returns a vector of atom handles that are hebbian adjacent to a given atom
 *
 * Calculated as all atoms that are adjacent to the given atom where the type
 * of the connecting edge is a hebbian link
 */
HandleSeq SimpleImportanceDiffusionAgent::hebbianAdjacentAtoms(Handle h)
{

}

/*
 * Returns a map of atom handles and probability values
 *
 * Calculated as the portion of the total STI that will be allocated to each
 * of the atoms
 */
std::map<Handle, double> SimpleImportanceDiffusionAgent::probabilityVector(
        HandleSeq handles)
{

}

/*
 * Returns the total amount of STI that the atom will diffuse
 *
 * Calculated as the maximum spread percentage multiplied by the atom's STI
 */
AttentionValue::sti_t SimpleImportanceDiffusionAgent::calculateDiffusionAmount(Handle h)
{

}

} // namespace
<commit_msg>Update formatting<commit_after>/*
 * opencog/dynamics/attention/SimpleImportanceDiffusionAgent.cc
 *
 * Copyright (C) 2014 Cosmo Harrigan
 * All Rights Reserved
 *
 * Written by Cosmo Harrigan
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "SimpleImportanceDiffusionAgent.h"
#include "SpreadDecider.h"
#include <time.h>
#include <math.h>
#include <opencog/atomspace/Link.h>
#include <opencog/dynamics/attention/atom_types.h>
#include <opencog/server/CogServer.h>
#include <opencog/util/Config.h>
#include <opencog/util/platform.h>
#include <opencog/util/mt19937ar.h>
#include <opencog/util/algorithm.h>

#define DEBUG
namespace opencog
{

SimpleImportanceDiffusionAgent::SimpleImportanceDiffusionAgent(CogServer& cs) :
    Agent(cs)
{
    static const std::string defaultConfig[] = {
        //! Maximum percentage of STI that is spread from an atom
        "ECAN_MAX_SPREAD_PERCENTAGE","0.5",
        "ECAN_SPREAD_HEBBIAN_ONLY","false",
        "",""
    };

    setParameters(defaultConfig);
    spreadDecider = NULL;
    maxSpreadPercentage =
            (float) (config().get_double("ECAN_MAX_SPREAD_PERCENTAGE"));
    setSpreadDecider(STEP);
    spreadHebbianOnly = config().get_bool("ECAN_SPREAD_HEBBIAN_ONLY");

    // Provide a logger
    log = NULL;
    setLogger(new opencog::Logger("SimpleImportanceDiffusionAgent.log",
                                  Logger::FINE, true));
}

void SimpleImportanceDiffusionAgent::setLogger(Logger* _log)
{
    if (log) delete log;
    log = _log;
}

Logger* SimpleImportanceDiffusionAgent::getLogger()
{
    return log;
}

void SimpleImportanceDiffusionAgent::setSpreadDecider(int type, float shape)
{
    if (spreadDecider) {
        delete spreadDecider;
        spreadDecider = NULL;
    }
    switch (type) {
    case HYPERBOLIC:
        spreadDecider =
                (SpreadDecider*) new HyperbolicDecider(_cogserver, shape);
        break;
    case STEP:
        spreadDecider = (SpreadDecider*) new StepDecider(_cogserver);
        break;
    }

}

SimpleImportanceDiffusionAgent::~SimpleImportanceDiffusionAgent()
{
    if (spreadDecider) {
        delete spreadDecider;
        spreadDecider = NULL;
    }
}

void SimpleImportanceDiffusionAgent::run()
{
    as = &_cogserver.getAtomSpace();
    spreadDecider->setFocusBoundary(0);
    spreadImportance();
}

void SimpleImportanceDiffusionAgent::spreadImportance()
{
    HandleSeq diffusionSourceVector =
            SimpleImportanceDiffusionAgent::diffusionSourceVector();

    foreach (Handle atomSource, diffusionSourceVector)
    {
        // Check the decision function to determine if spreading will occur
        if (spreadDecider->spreadDecision(as->getSTI(atomSource))) {
            diffuseAtom(atomSource);
        }
    }
}

/*
 * Diffuses importance from one atom to its non-hebbian incident atoms
 * and hebbian adjacent atoms
 */
void SimpleImportanceDiffusionAgent::diffuseAtom(Handle atomSource)
{
    // TODO: What if the STI values of the atoms change during these updates?
    // This could go wrong if there were simultaneous updates in other threads.

    HandleSeq targetAtoms =
            SimpleImportanceDiffusionAgent::incidentAtoms(atomSource);

    HandleSeq hebbianAdjacentAtoms =
            SimpleImportanceDiffusionAgent::hebbianAdjacentAtoms(atomSource);

    targetAtoms.insert(targetAtoms.end(),
                       hebbianAdjacentAtoms.begin(),
                       hebbianAdjacentAtoms.end());

    std::map<Handle, double> probabilityVector =
            SimpleImportanceDiffusionAgent::probabilityVector(targetAtoms);

    AttentionValue::sti_t diffusionAmount =
            SimpleImportanceDiffusionAgent::calculateDiffusionAmount(atomSource);

    if (diffusionAmount == 0)
    {
        return;
    }

    // For each atomTarget in incident atoms
    foreach (Handle atomTarget, targetAtoms)
    {
        diffusionAmount = diffusionAmount * probabilityVector[atomTarget];

        // Trade STI between the source and target atoms
        as->setSTI(atomSource, as->getSTI(atomSource) - diffusionAmount);
        as->setSTI(atomTarget, as->getSTI(atomTarget) + diffusionAmount);

        // TODO: How to make this a transaction? This could go wrong if there
        // were simultaneous updates in other threads.
    }
}

/*
 * Returns a vector of atom handles that will diffuse STI
 *
 * Calculated as all atoms in the attentional focus (nodes and links)
 * excluding any hebbian links
 */
HandleSeq SimpleImportanceDiffusionAgent::diffusionSourceVector()
{

}

/*
 * Returns a vector of atom handles that are incident to a given atom
 *
 * Calculated as the set union of an atom's incoming and outgoing set,
 * excluding hebbian links
 */
HandleSeq SimpleImportanceDiffusionAgent::incidentAtoms(Handle h)
{

}

/*
 * Returns a vector of atom handles that are hebbian adjacent to a given atom
 *
 * Calculated as all atoms that are adjacent to the given atom where the type
 * of the connecting edge is a hebbian link
 */
HandleSeq SimpleImportanceDiffusionAgent::hebbianAdjacentAtoms(Handle h)
{

}

/*
 * Returns a map of atom handles and probability values
 *
 * Calculated as the portion of the total STI that will be allocated to each
 * of the atoms
 */
std::map<Handle, double> SimpleImportanceDiffusionAgent::probabilityVector(
        HandleSeq handles)
{

}

/*
 * Returns the total amount of STI that the atom will diffuse
 *
 * Calculated as the maximum spread percentage multiplied by the atom's STI
 */
AttentionValue::sti_t SimpleImportanceDiffusionAgent::calculateDiffusionAmount(
        Handle h)
{

}

} // namespace
<|endoftext|>


<commit_before><commit_msg>rss-open-source-library-fix-cpplint-issue<commit_after><|endoftext|>


<commit_before>
#include "Internal.hpp"
#include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
#include <BulletCollision/CollisionDispatch/btManifoldResult.h>
#include <BulletSoftBody/btSoftRigidDynamicsWorld.h>
#include <BulletSoftBody/btSoftBodyHelpers.h>
#include <BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h>
#include <BulletCollision/CollisionDispatch/btSimulationIslandManager.h>
#include <BulletCollision/CollisionDispatch/btGhostObject.h>

#include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
#include <BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>

#include <LuminoEngine/Rendering/RenderingContext.hpp>
#include <LuminoEngine/Physics/PhysicsObject.hpp>
#include <LuminoEngine/Physics/RigidBody.hpp>
#include <LuminoEngine/Physics/PhysicsWorld.hpp>

namespace ln {

//==============================================================================
// PhysicsDebugRenderer
namespace detail {

class PhysicsDebugRenderer
    : public btIDebugDraw
{
public:
    RenderingContext* context = nullptr;

    virtual void drawLine(const btVector3& from, const btVector3& to, const btVector3& color) override
    {
        drawLine(from, to, color, color);
    }

    virtual void drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor) override
    {
        printf("drawLine\n");
        context->drawLine(
            Vector3(from.getX(), from.getY(), from.getZ()),
            Color(fromColor.getX(), fromColor.getY(), fromColor.getZ()),
            Vector3(to.getX(), to.getY(), to.getZ()),
            Color(toColor.getX(), toColor.getY(), toColor.getZ(), 1.0f));
    }

    virtual void drawContactPoint(const btVector3& PointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color) override
    {
    }

    virtual void reportErrorWarning(const char* warningString) override
    {
        printf("Physics: %s\n", warningString);	// TODO
    }

    virtual void draw3dText(const btVector3& location, const char* textString) override
    {
    }

    virtual void setDebugMode(int debugMode) override
    {
        m_debugMode = debugMode;
    }

    virtual int getDebugMode() const override
    {
        return m_debugMode;
    }

private:
    int m_debugMode = 0;
};

} // namespace detail

//==============================================================================
// PhysicsWorld

PhysicsWorld::PhysicsWorld()
{
}

PhysicsWorld::~PhysicsWorld()
{
}

void PhysicsWorld::initialize()
{
    Object::initialize();

    // ↓以下で出てくる単語とか
    //		http://nullorempry.jimdo.com/2012/03/10/bullet-physics%E3%81%AE%E3%81%8A%E5%8B%89%E5%BC%B7/

    int maxNumOutstandingTasks = 4;

#ifdef LN_USE_PARALLEL
    m_threadSupportCollision = new Win32ThreadSupport(Win32ThreadSupport::Win32ThreadConstructionInfo(
        "collision",
        processCollisionTask,
        createCollisionLocalStoreMemory,
        maxNumOutstandingTasks));
#endif

    // カスタムコリジョンコールバック
    //gContactAddedCallback = CustomMaterialCombinerCallback;

    // コリジョンコンフィグ
    btDefaultCollisionConstructionInfo defaultCollisionConstructionInfo;
    //defaultCollisionConstructionInfo.m_defaultMaxPersistentManifoldPoolSize = 32768;
    m_btCollisionConfig = new btDefaultCollisionConfiguration(defaultCollisionConstructionInfo);
    // ソフトボディ使うとき
    //m_btCollisionConfig = LN_NEW btSoftBodyRigidBodyCollisionConfiguration();//

    // コリジョンディスパッチャ
#ifdef LN_USE_PARALLEL
    m_btCollisionDispatcher = new SpuGatheringCollisionDispatcher(m_threadSupportCollision, maxNumOutstandingTasks, m_btCollisionConfig);
#else
    m_btCollisionDispatcher = LN_NEW btCollisionDispatcher(m_btCollisionConfig);
#endif

    // ブロードフェーズアルゴリズム (衝突検出のためのグループ分けアルゴリズム)
    /*
    btDbvtBroadphase				AABB木による高速で動的な階層空間を使います。
    btAxisSweep3 bt32BitAxisSweep3	逐次3Dスイープ&プルーン(incremental 3D sweep and prune)を実装しています。
    btCudaBroadphase				GPUを使用した高速な一様グリッドを実装しています。http://bulletjpn.web.fc2.com/05_BulletCollisionDetection.html
    */
    //   btVector3	btv3WorldAabbMin( -3000.0f, -3000.0f, -3000.0f );
    //btVector3	btv3WorldAabbMax(  3000.0f,  3000.0f,  3000.0f );
    //int			iMaxProxies = 32766;//5 * 5 * 5 + 1024;//
    //m_btBroadphase = new btAxisSweep3( btv3WorldAabbMin, btv3WorldAabbMax, iMaxProxies );
    m_btBroadphase = LN_NEW btDbvtBroadphase();

    // ソルバ
#ifdef LN_USE_PARALLEL
    m_threadSupportSolver = createSolverThreadSupport(maxNumOutstandingTasks);
    m_btSolver = new btParallelConstraintSolver(m_threadSupportSolver);
    //this solver requires the contacts to be in a contiguous pool, so avoid dynamic allocation
    m_btCollisionDispatcher->setDispatcherFlags(btCollisionDispatcher::CD_DISABLE_CONTACTPOOL_DYNAMIC_ALLOCATION);
#else
    m_btSolver = new btSequentialImpulseConstraintSolver();
#endif

    // ワールドの作成
    //m_btWorld = LN_NEW btSoftRigidDynamicsWorld( m_btCollisionDispatcher, m_btBroadphase, m_btSolver, m_btCollisionConfig, NULL );
    m_btWorld = new btDiscreteDynamicsWorld(m_btCollisionDispatcher, m_btBroadphase, m_btSolver, m_btCollisionConfig);

    m_debugRenderer = std::make_unique<detail::PhysicsDebugRenderer>();
    m_btWorld->setDebugDrawer(m_debugRenderer.get());
    //m_btWorld->setInternalTickCallback(pickingPreTickCallback,this,true);
    //m_btWorld->getDispatchInfo().m_enableSPU = true;

    //btOverlapFilterCallback * filterCallback = new FilterCallback();
    //m_btWorld->getPairCache()->setOverlapFilterCallback( &gFilterCallback );

#if 0	// MMM setting (デフォルトは 10)
    m_btWorld->getSolverInfo().m_numIterations = 4;
#endif
#ifdef LN_USE_PARALLEL
    m_btWorld->getSimulationIslandManager()->setSplitIslands(false);
    m_btWorld->getSolverInfo().m_numIterations = 4;
    m_btWorld->getSolverInfo().m_solverMode = SOLVER_SIMD + SOLVER_USE_WARMSTARTING;//+SOLVER_RANDMIZE_ORDER;

    m_btWorld->getDispatchInfo().m_enableSPU = true;
#endif
    //m_btWorld->setDebugDrawer(&gDebugDrawer);

    // 重力		TODO: 初期値
    m_btWorld->setGravity(btVector3(0.0f, -9.81f, 0.0f));

    m_btGhostPairCallback = new btGhostPairCallback();
    m_btWorld->getPairCache()->setInternalGhostPairCallback(m_btGhostPairCallback);

    m_softBodyWorldInfo = new btSoftBodyWorldInfo();
    m_softBodyWorldInfo->air_density = 1.2f;
    m_softBodyWorldInfo->water_density = 0;
    m_softBodyWorldInfo->water_offset = 0;
    m_softBodyWorldInfo->water_normal = btVector3(0.0f, 0.0f, 0.0f);
    m_softBodyWorldInfo->m_gravity = m_btWorld->getGravity();
    m_softBodyWorldInfo->m_broadphase = m_btBroadphase;
    m_softBodyWorldInfo->m_dispatcher = m_btCollisionDispatcher;
    m_softBodyWorldInfo->m_sparsesdf.Initialize();
    m_softBodyWorldInfo->m_sparsesdf.Reset();
}

void PhysicsWorld::dispose()
{
    Object::dispose();

    LN_SAFE_DELETE(m_softBodyWorldInfo);
    LN_SAFE_DELETE(m_btGhostPairCallback);
    LN_SAFE_DELETE(m_btWorld);
    LN_SAFE_DELETE(m_btSolver);
    LN_SAFE_DELETE(m_btBroadphase);
    LN_SAFE_DELETE(m_btCollisionDispatcher);
    LN_SAFE_DELETE(m_btCollisionConfig);
}

void PhysicsWorld::addPhysicsObject(PhysicsObject* physicsObject)
{
    if (LN_REQUIRE(physicsObject)) return;
    if (LN_REQUIRE(!physicsObject->physicsWorld())) return;
    m_physicsObjectList.add(physicsObject);
    //addObjectInternal(physicsObject);
    physicsObject->setPhysicsWorld(this);
}

void PhysicsWorld::removePhysicsObject(PhysicsObject* physicsObject)
{
    if (LN_REQUIRE(physicsObject)) return;
    if (LN_REQUIRE(physicsObject->physicsWorld() == this)) return;
    m_physicsObjectList.remove(physicsObject);
    removeObjectInternal(physicsObject);
    physicsObject->setPhysicsWorld(nullptr);
}

void PhysicsWorld::stepSimulation(float elapsedSeconds)
{
    for (auto& obj : m_physicsObjectList) {
    	obj->onBeforeStepSimulation();
    }

    // TODO: FPS を Engine からもらう
    const float internalTimeUnit = 1.0f / 60.0f;

    // http://d.hatena.ne.jp/ousttrue/20100425/1272165711
    // m_elapsedTime が 1.0(1秒) より小さい間は 16ms を最大 60 回繰り返して追いつこうとする設定。
    // m_elapsedTime が 1.0 を超えている場合は追いつけずに、物体の移動が遅くなる。
    // FIXME: MMD
    //m_btWorld->stepSimulation(elapsedTime, 120, 0.008333334f);
    m_btWorld->stepSimulation(elapsedSeconds, 1, internalTimeUnit);

    // m_elapsedTime が 16ms より大きい場合は、1回 16ms 分のシミュレーションを可能な限り繰り返して m_elapsedTime に追いついていく設定。
    // 遅れるほど計算回数が増えるので、最終的に破綻するかもしれない。
    //m_btWorld->stepSimulation(m_elapsedTime, 1 + (int)(m_elapsedTime / internalUnit), internalUnit);

    for (auto& obj : m_physicsObjectList) {
        obj->onAfterStepSimulation();
    }
}

void PhysicsWorld::renderDebug(RenderingContext* context)
{
    m_debugRenderer->context = context;
    m_btWorld->getDebugDrawer()->setDebugMode(btIDebugDraw::DBG_DrawWireframe);
    printf("----\n");
    m_btWorld->debugDrawWorld();
    printf("----\n");
}

void PhysicsWorld::addObjectInternal(PhysicsObject* obj)
{
    switch (obj->physicsObjectType())
    {
    case PhysicsObjectType::RigidBody:
        m_btWorld->addRigidBody(static_cast<RigidBody*>(obj)->body());
        break;
    default:
        LN_UNREACHABLE();
        break;
    }
}

void PhysicsWorld::removeObjectInternal(PhysicsObject* obj)
{
    switch (obj->physicsObjectType())
    {
    case PhysicsObjectType::RigidBody:
        m_btWorld->addRigidBody(static_cast<RigidBody*>(obj)->body());
        break;
    default:
        LN_UNREACHABLE();
        break;
    }
}

} // namespace ln

<commit_msg>renderDebug<commit_after>
#include "Internal.hpp"
#include <BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h>
#include <BulletCollision/CollisionDispatch/btManifoldResult.h>
#include <BulletSoftBody/btSoftRigidDynamicsWorld.h>
#include <BulletSoftBody/btSoftBodyHelpers.h>
#include <BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h>
#include <BulletCollision/CollisionDispatch/btSimulationIslandManager.h>
#include <BulletCollision/CollisionDispatch/btGhostObject.h>

#include <BulletCollision/BroadphaseCollision/btDbvtBroadphase.h>
#include <BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h>

#include <LuminoEngine/Rendering/RenderingContext.hpp>
#include <LuminoEngine/Physics/PhysicsObject.hpp>
#include <LuminoEngine/Physics/RigidBody.hpp>
#include <LuminoEngine/Physics/PhysicsWorld.hpp>

namespace ln {

//==============================================================================
// PhysicsDebugRenderer
namespace detail {

class PhysicsDebugRenderer
    : public btIDebugDraw
{
public:
    RenderingContext* context = nullptr;

    virtual void drawLine(const btVector3& from, const btVector3& to, const btVector3& color) override
    {
        drawLine(from, to, color, color);
    }

    virtual void drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor) override
    {
        context->drawLine(
            Vector3(from.getX(), from.getY(), from.getZ()),
            Color(fromColor.getX(), fromColor.getY(), fromColor.getZ()),
            Vector3(to.getX(), to.getY(), to.getZ()),
            Color(toColor.getX(), toColor.getY(), toColor.getZ(), 1.0f));
    }

    virtual void drawContactPoint(const btVector3& PointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color) override
    {
    }

    virtual void reportErrorWarning(const char* warningString) override
    {
        printf("Physics: %s\n", warningString);	// TODO
    }

    virtual void draw3dText(const btVector3& location, const char* textString) override
    {
    }

    virtual void setDebugMode(int debugMode) override
    {
        m_debugMode = debugMode;
    }

    virtual int getDebugMode() const override
    {
        return m_debugMode;
    }

private:
    int m_debugMode = 0;
};

} // namespace detail

//==============================================================================
// PhysicsWorld

PhysicsWorld::PhysicsWorld()
{
}

PhysicsWorld::~PhysicsWorld()
{
}

void PhysicsWorld::initialize()
{
    Object::initialize();

    // ↓以下で出てくる単語とか
    //		http://nullorempry.jimdo.com/2012/03/10/bullet-physics%E3%81%AE%E3%81%8A%E5%8B%89%E5%BC%B7/

    int maxNumOutstandingTasks = 4;

#ifdef LN_USE_PARALLEL
    m_threadSupportCollision = new Win32ThreadSupport(Win32ThreadSupport::Win32ThreadConstructionInfo(
        "collision",
        processCollisionTask,
        createCollisionLocalStoreMemory,
        maxNumOutstandingTasks));
#endif

    // カスタムコリジョンコールバック
    //gContactAddedCallback = CustomMaterialCombinerCallback;

    // コリジョンコンフィグ
    btDefaultCollisionConstructionInfo defaultCollisionConstructionInfo;
    //defaultCollisionConstructionInfo.m_defaultMaxPersistentManifoldPoolSize = 32768;
    m_btCollisionConfig = new btDefaultCollisionConfiguration(defaultCollisionConstructionInfo);
    // ソフトボディ使うとき
    //m_btCollisionConfig = LN_NEW btSoftBodyRigidBodyCollisionConfiguration();//

    // コリジョンディスパッチャ
#ifdef LN_USE_PARALLEL
    m_btCollisionDispatcher = new SpuGatheringCollisionDispatcher(m_threadSupportCollision, maxNumOutstandingTasks, m_btCollisionConfig);
#else
    m_btCollisionDispatcher = LN_NEW btCollisionDispatcher(m_btCollisionConfig);
#endif

    // ブロードフェーズアルゴリズム (衝突検出のためのグループ分けアルゴリズム)
    /*
    btDbvtBroadphase				AABB木による高速で動的な階層空間を使います。
    btAxisSweep3 bt32BitAxisSweep3	逐次3Dスイープ&プルーン(incremental 3D sweep and prune)を実装しています。
    btCudaBroadphase				GPUを使用した高速な一様グリッドを実装しています。http://bulletjpn.web.fc2.com/05_BulletCollisionDetection.html
    */
    //   btVector3	btv3WorldAabbMin( -3000.0f, -3000.0f, -3000.0f );
    //btVector3	btv3WorldAabbMax(  3000.0f,  3000.0f,  3000.0f );
    //int			iMaxProxies = 32766;//5 * 5 * 5 + 1024;//
    //m_btBroadphase = new btAxisSweep3( btv3WorldAabbMin, btv3WorldAabbMax, iMaxProxies );
    m_btBroadphase = LN_NEW btDbvtBroadphase();

    // ソルバ
#ifdef LN_USE_PARALLEL
    m_threadSupportSolver = createSolverThreadSupport(maxNumOutstandingTasks);
    m_btSolver = new btParallelConstraintSolver(m_threadSupportSolver);
    //this solver requires the contacts to be in a contiguous pool, so avoid dynamic allocation
    m_btCollisionDispatcher->setDispatcherFlags(btCollisionDispatcher::CD_DISABLE_CONTACTPOOL_DYNAMIC_ALLOCATION);
#else
    m_btSolver = new btSequentialImpulseConstraintSolver();
#endif

    // ワールドの作成
    //m_btWorld = LN_NEW btSoftRigidDynamicsWorld( m_btCollisionDispatcher, m_btBroadphase, m_btSolver, m_btCollisionConfig, NULL );
    m_btWorld = new btDiscreteDynamicsWorld(m_btCollisionDispatcher, m_btBroadphase, m_btSolver, m_btCollisionConfig);

    m_debugRenderer = std::make_unique<detail::PhysicsDebugRenderer>();
    m_btWorld->setDebugDrawer(m_debugRenderer.get());
    //m_btWorld->setInternalTickCallback(pickingPreTickCallback,this,true);
    //m_btWorld->getDispatchInfo().m_enableSPU = true;

    //btOverlapFilterCallback * filterCallback = new FilterCallback();
    //m_btWorld->getPairCache()->setOverlapFilterCallback( &gFilterCallback );

#if 0	// MMM setting (デフォルトは 10)
    m_btWorld->getSolverInfo().m_numIterations = 4;
#endif
#ifdef LN_USE_PARALLEL
    m_btWorld->getSimulationIslandManager()->setSplitIslands(false);
    m_btWorld->getSolverInfo().m_numIterations = 4;
    m_btWorld->getSolverInfo().m_solverMode = SOLVER_SIMD + SOLVER_USE_WARMSTARTING;//+SOLVER_RANDMIZE_ORDER;

    m_btWorld->getDispatchInfo().m_enableSPU = true;
#endif
    //m_btWorld->setDebugDrawer(&gDebugDrawer);

    // 重力		TODO: 初期値
    m_btWorld->setGravity(btVector3(0.0f, -9.81f, 0.0f));

    m_btGhostPairCallback = new btGhostPairCallback();
    m_btWorld->getPairCache()->setInternalGhostPairCallback(m_btGhostPairCallback);

    m_softBodyWorldInfo = new btSoftBodyWorldInfo();
    m_softBodyWorldInfo->air_density = 1.2f;
    m_softBodyWorldInfo->water_density = 0;
    m_softBodyWorldInfo->water_offset = 0;
    m_softBodyWorldInfo->water_normal = btVector3(0.0f, 0.0f, 0.0f);
    m_softBodyWorldInfo->m_gravity = m_btWorld->getGravity();
    m_softBodyWorldInfo->m_broadphase = m_btBroadphase;
    m_softBodyWorldInfo->m_dispatcher = m_btCollisionDispatcher;
    m_softBodyWorldInfo->m_sparsesdf.Initialize();
    m_softBodyWorldInfo->m_sparsesdf.Reset();
}

void PhysicsWorld::dispose()
{
    Object::dispose();

    LN_SAFE_DELETE(m_softBodyWorldInfo);
    LN_SAFE_DELETE(m_btGhostPairCallback);
    LN_SAFE_DELETE(m_btWorld);
    LN_SAFE_DELETE(m_btSolver);
    LN_SAFE_DELETE(m_btBroadphase);
    LN_SAFE_DELETE(m_btCollisionDispatcher);
    LN_SAFE_DELETE(m_btCollisionConfig);
}

void PhysicsWorld::addPhysicsObject(PhysicsObject* physicsObject)
{
    if (LN_REQUIRE(physicsObject)) return;
    if (LN_REQUIRE(!physicsObject->physicsWorld())) return;
    m_physicsObjectList.add(physicsObject);
    //addObjectInternal(physicsObject);
    physicsObject->setPhysicsWorld(this);
}

void PhysicsWorld::removePhysicsObject(PhysicsObject* physicsObject)
{
    if (LN_REQUIRE(physicsObject)) return;
    if (LN_REQUIRE(physicsObject->physicsWorld() == this)) return;
    m_physicsObjectList.remove(physicsObject);
    removeObjectInternal(physicsObject);
    physicsObject->setPhysicsWorld(nullptr);
}

void PhysicsWorld::stepSimulation(float elapsedSeconds)
{
    for (auto& obj : m_physicsObjectList) {
    	obj->onBeforeStepSimulation();
    }

    // TODO: FPS を Engine からもらう
    const float internalTimeUnit = 1.0f / 60.0f;

    // http://d.hatena.ne.jp/ousttrue/20100425/1272165711
    // m_elapsedTime が 1.0(1秒) より小さい間は 16ms を最大 60 回繰り返して追いつこうとする設定。
    // m_elapsedTime が 1.0 を超えている場合は追いつけずに、物体の移動が遅くなる。
    // FIXME: MMD
    //m_btWorld->stepSimulation(elapsedTime, 120, 0.008333334f);
    m_btWorld->stepSimulation(elapsedSeconds, 1, internalTimeUnit);

    // m_elapsedTime が 16ms より大きい場合は、1回 16ms 分のシミュレーションを可能な限り繰り返して m_elapsedTime に追いついていく設定。
    // 遅れるほど計算回数が増えるので、最終的に破綻するかもしれない。
    //m_btWorld->stepSimulation(m_elapsedTime, 1 + (int)(m_elapsedTime / internalUnit), internalUnit);

    for (auto& obj : m_physicsObjectList) {
        obj->onAfterStepSimulation();
    }
}

void PhysicsWorld::renderDebug(RenderingContext* context)
{
    context->pushState();
    m_debugRenderer->context = context;
    m_btWorld->getDebugDrawer()->setDebugMode(btIDebugDraw::DBG_DrawWireframe);
    m_btWorld->debugDrawWorld();
    context->popState();
}

void PhysicsWorld::addObjectInternal(PhysicsObject* obj)
{
    switch (obj->physicsObjectType())
    {
    case PhysicsObjectType::RigidBody:
        m_btWorld->addRigidBody(static_cast<RigidBody*>(obj)->body());
        break;
    default:
        LN_UNREACHABLE();
        break;
    }
}

void PhysicsWorld::removeObjectInternal(PhysicsObject* obj)
{
    switch (obj->physicsObjectType())
    {
    case PhysicsObjectType::RigidBody:
        m_btWorld->addRigidBody(static_cast<RigidBody*>(obj)->body());
        break;
    default:
        LN_UNREACHABLE();
        break;
    }
}

} // namespace ln

<|endoftext|>


<commit_before>#include "FlowJunction.h"
#include "Factory.h"
#include "Conversion.h"
#include "Simulation.h"
#include "FEProblem.h"
#include "GeometricalComponent.h"
#include <sstream>

template<>
InputParameters validParams<FlowJunction>()
{
  InputParameters params = validParams<Junction>();
  params.addParam<std::vector<Real> >("K", "Form loss coefficients");
  params.addRequiredParam<UserObjectName>("eos", "The name of equation of state object to use.");
  return params;
}

FlowJunction::FlowJunction(const std::string & name, InputParameters params) :
    Junction(name, params),
    _lm_name(genName("lm", _id, "")),
    _K(getParam<std::vector<Real> >("K"))
{
}

FlowJunction::~FlowJunction()
{
}

void
FlowJunction::addVariables()
{
  // add scalar variable (i.e. Lagrange multiplier)
  switch (_model_type)
  {
  case FlowModel::EQ_MODEL_2:
    _sim.addVariable(true, _lm_name,  FEType(SECOND, SCALAR), 0, 1.);
    break;

  case FlowModel::EQ_MODEL_3:
    _sim.addVariable(true, _lm_name,  FEType(THIRD, SCALAR), 0, 1.e-5);
    break;

  default:
    mooseError("Not implemented yet.");
    break;
  }
}

void
FlowJunction::addMooseObjects()
{
  std::vector<VariableName> cv_area(1, FlowModel::AREA);
  std::vector<VariableName> cv_u(1, FlowModel::VELOCITY);
  std::vector<VariableName> cv_pressure(1, FlowModel::PRESSURE);
  std::vector<VariableName> cv_enthalpy(1, FlowModel::ENTHALPY);
  std::vector<VariableName> cv_rho(1, FlowModel::RHO);
  std::vector<VariableName> cv_rhou(1, FlowModel::RHOU);
  std::vector<VariableName> cv_rhoE(1, FlowModel::RHOE);
  std::vector<VariableName> cv_lambda(1, _lm_name);
  std::vector<Real> sf = _sim.getParam<std::vector<Real> >("scaling_factors");

  MooseEnum execute_options(SetupInterface::getExecuteOptions());
  execute_options = "residual";

  std::string c_pps = genName("flow", _id, "_pps");
  {
    InputParameters params = _factory.getValidParams("PressureCB");
    params.set<std::vector<unsigned int> >("r7:boundary") = _bnd_id;
    params.set<MooseEnum>("execute_on") = execute_options;
    params.set<MooseEnum>("output") = "none";
    params.set<UserObjectName>("eos")  = getParam<UserObjectName>("eos");
    // coupling
    params.set<std::vector<VariableName> >("area") = cv_area;
    params.set<std::vector<VariableName> >("pressure") = cv_pressure;

    _sim.addPostprocessor("PressureCB", c_pps, params);
  }

  // add BC terms
  for (unsigned int i = 0; i < _bnd_id.size(); ++i)
  {
    std::vector<unsigned int> bnd_id(1, _bnd_id[i]);

    // mass
    {
      InputParameters params = _factory.getValidParams("OneDFreeMassBC");
      params.set<NonlinearVariableName>("variable") = FlowModel::RHO;
      params.set<std::vector<unsigned int> >("r7:boundary") = bnd_id;
      params.set<UserObjectName>("eos") = getParam<UserObjectName>("eos");
      params.set<PostprocessorName>("c") = c_pps;
      params.set<std::vector<Real> >("scaling_factors") = sf;
      // coupling
      params.set<std::vector<VariableName> >("rho") = cv_rho;
      params.set<std::vector<VariableName> >("rhou") = cv_rhou;
      params.set<std::vector<VariableName> >("u") = cv_u;
      params.set<std::vector<VariableName> >("area") = cv_area;
      params.set<std::vector<VariableName> >("lambda") = cv_lambda;

      if (_model_type == FlowModel::EQ_MODEL_3)
      {
        params.set<std::vector<VariableName> >("rhoE") = cv_rhoE;
        params.set<std::vector<VariableName> >("enthalpy") = cv_enthalpy;
      }

      _sim.addBoundaryCondition("OneDFreeMassBC", genName("mass", _id, "_bc"), params);
    }
    // momentum
    {
      InputParameters params = _factory.getValidParams("OneDFreeMomentumBC");
      params.set<NonlinearVariableName>("variable") = FlowModel::RHOU;
      params.set<std::vector<unsigned int> >("r7:boundary") = bnd_id;
      params.set<UserObjectName>("eos") = getParam<UserObjectName>("eos");
      params.set<PostprocessorName>("c") = c_pps;
      params.set<std::vector<Real> >("scaling_factors") = sf;
      // coupling
      params.set<std::vector<VariableName> >("rho") = cv_rho;
      params.set<std::vector<VariableName> >("rhou") = cv_rhou;
      params.set<std::vector<VariableName> >("u") = cv_u;
      params.set<std::vector<VariableName> >("pressure") = cv_pressure;
      params.set<std::vector<VariableName> >("area") = cv_area;
      params.set<std::vector<VariableName> >("lambda") = cv_lambda;

      if (_model_type == FlowModel::EQ_MODEL_3)
      {
        params.set<std::vector<VariableName> >("rhoE") = cv_rhoE;
        params.set<std::vector<VariableName> >("enthalpy") = cv_enthalpy;
      }

      _sim.addBoundaryCondition("OneDFreeMomentumBC", genName("mom", _id, "_bc"), params);
    }
    // energy
    if (_model_type == FlowModel::EQ_MODEL_3)
    {
      InputParameters params = _factory.getValidParams("OneDFreeEnergyBC");
      params.set<NonlinearVariableName>("variable") = FlowModel::RHOE;
      params.set<std::vector<unsigned int> >("r7:boundary") = bnd_id;
      params.set<UserObjectName>("eos") = getParam<UserObjectName>("eos");
      // coupling
      params.set<std::vector<VariableName> >("rho") = cv_rho;
      params.set<std::vector<VariableName> >("rhou") = cv_rhou;
      params.set<std::vector<VariableName> >("rhoE") = cv_rhoE;
      params.set<std::vector<VariableName> >("u") = cv_u;
      params.set<std::vector<VariableName> >("enthalpy") = cv_enthalpy;
      params.set<std::vector<VariableName> >("area") = cv_area;
      params.set<PostprocessorName>("c") = c_pps;
      params.set<std::vector<Real> >("scaling_factors") = sf;
      params.set<std::vector<VariableName> >("lambda") = cv_lambda;

      _sim.addBoundaryCondition("OneDFreeEnergyBC", genName("erg", _id, "_bc"), params);
    }
  }

  // add the constraints
  {
    InputParameters params = _factory.getValidParams("FlowConstraint");
    params.set<NonlinearVariableName>("variable") = _lm_name;

    params.set<FlowModel::EModelType>("model_type") = _model_type;
    params.set<std::vector<unsigned int> >("nodes") = _nodes;
    params.set<std::vector<Real> >("normals") = _normals;
    params.set<std::vector<Real> >("K") = _K;
    params.set<UserObjectName>("eos") = getParam<UserObjectName>("eos");
    // coupling
    params.set<std::vector<VariableName> >("u") = cv_u;
    params.set<std::vector<VariableName> >("p") = cv_pressure;
    params.set<std::vector<VariableName> >("rho") = cv_rho;
    params.set<std::vector<VariableName> >("rhou") = cv_rhou;
    params.set<std::vector<VariableName> >("area") = cv_area;
    if (_model_type == FlowModel::EQ_MODEL_3)
    {
      params.set<std::vector<VariableName> >("rhoE") = cv_rhoE;
      params.set<std::vector<VariableName> >("enthalpy") = cv_enthalpy;
    }

    _sim.addScalarKernel("FlowConstraint", genName("flow", _id, "_c0"), params);
  }
}
<commit_msg>Just a cosmetic change on variable name used in FlowJunction component (refs idaholab/relap-7#2293)<commit_after>#include "FlowJunction.h"
#include "Factory.h"
#include "Conversion.h"
#include "Simulation.h"
#include "FEProblem.h"
#include "GeometricalComponent.h"
#include <sstream>

template<>
InputParameters validParams<FlowJunction>()
{
  InputParameters params = validParams<Junction>();
  params.addParam<std::vector<Real> >("K", "Form loss coefficients");
  params.addRequiredParam<UserObjectName>("eos", "The name of equation of state object to use.");
  return params;
}

FlowJunction::FlowJunction(const std::string & name, InputParameters params) :
    Junction(name, params),
    _lm_name(genName(name, "lm")),
    _K(getParam<std::vector<Real> >("K"))
{
}

FlowJunction::~FlowJunction()
{
}

void
FlowJunction::addVariables()
{
  // add scalar variable (i.e. Lagrange multiplier)
  switch (_model_type)
  {
  case FlowModel::EQ_MODEL_2:
    _sim.addVariable(true, _lm_name,  FEType(SECOND, SCALAR), 0, 1.);
    break;

  case FlowModel::EQ_MODEL_3:
    _sim.addVariable(true, _lm_name,  FEType(THIRD, SCALAR), 0, 1.e-5);
    break;

  default:
    mooseError("Not implemented yet.");
    break;
  }
}

void
FlowJunction::addMooseObjects()
{
  std::vector<VariableName> cv_area(1, FlowModel::AREA);
  std::vector<VariableName> cv_u(1, FlowModel::VELOCITY);
  std::vector<VariableName> cv_pressure(1, FlowModel::PRESSURE);
  std::vector<VariableName> cv_enthalpy(1, FlowModel::ENTHALPY);
  std::vector<VariableName> cv_rho(1, FlowModel::RHO);
  std::vector<VariableName> cv_rhou(1, FlowModel::RHOU);
  std::vector<VariableName> cv_rhoE(1, FlowModel::RHOE);
  std::vector<VariableName> cv_lambda(1, _lm_name);
  std::vector<Real> sf = _sim.getParam<std::vector<Real> >("scaling_factors");

  MooseEnum execute_options(SetupInterface::getExecuteOptions());
  execute_options = "residual";

  std::string c_pps = genName("flow", _id, "_pps");
  {
    InputParameters params = _factory.getValidParams("PressureCB");
    params.set<std::vector<unsigned int> >("r7:boundary") = _bnd_id;
    params.set<MooseEnum>("execute_on") = execute_options;
    params.set<MooseEnum>("output") = "none";
    params.set<UserObjectName>("eos")  = getParam<UserObjectName>("eos");
    // coupling
    params.set<std::vector<VariableName> >("area") = cv_area;
    params.set<std::vector<VariableName> >("pressure") = cv_pressure;

    _sim.addPostprocessor("PressureCB", c_pps, params);
  }

  // add BC terms
  for (unsigned int i = 0; i < _bnd_id.size(); ++i)
  {
    std::vector<unsigned int> bnd_id(1, _bnd_id[i]);

    // mass
    {
      InputParameters params = _factory.getValidParams("OneDFreeMassBC");
      params.set<NonlinearVariableName>("variable") = FlowModel::RHO;
      params.set<std::vector<unsigned int> >("r7:boundary") = bnd_id;
      params.set<UserObjectName>("eos") = getParam<UserObjectName>("eos");
      params.set<PostprocessorName>("c") = c_pps;
      params.set<std::vector<Real> >("scaling_factors") = sf;
      // coupling
      params.set<std::vector<VariableName> >("rho") = cv_rho;
      params.set<std::vector<VariableName> >("rhou") = cv_rhou;
      params.set<std::vector<VariableName> >("u") = cv_u;
      params.set<std::vector<VariableName> >("area") = cv_area;
      params.set<std::vector<VariableName> >("lambda") = cv_lambda;

      if (_model_type == FlowModel::EQ_MODEL_3)
      {
        params.set<std::vector<VariableName> >("rhoE") = cv_rhoE;
        params.set<std::vector<VariableName> >("enthalpy") = cv_enthalpy;
      }

      _sim.addBoundaryCondition("OneDFreeMassBC", genName("mass", _id, "_bc"), params);
    }
    // momentum
    {
      InputParameters params = _factory.getValidParams("OneDFreeMomentumBC");
      params.set<NonlinearVariableName>("variable") = FlowModel::RHOU;
      params.set<std::vector<unsigned int> >("r7:boundary") = bnd_id;
      params.set<UserObjectName>("eos") = getParam<UserObjectName>("eos");
      params.set<PostprocessorName>("c") = c_pps;
      params.set<std::vector<Real> >("scaling_factors") = sf;
      // coupling
      params.set<std::vector<VariableName> >("rho") = cv_rho;
      params.set<std::vector<VariableName> >("rhou") = cv_rhou;
      params.set<std::vector<VariableName> >("u") = cv_u;
      params.set<std::vector<VariableName> >("pressure") = cv_pressure;
      params.set<std::vector<VariableName> >("area") = cv_area;
      params.set<std::vector<VariableName> >("lambda") = cv_lambda;

      if (_model_type == FlowModel::EQ_MODEL_3)
      {
        params.set<std::vector<VariableName> >("rhoE") = cv_rhoE;
        params.set<std::vector<VariableName> >("enthalpy") = cv_enthalpy;
      }

      _sim.addBoundaryCondition("OneDFreeMomentumBC", genName("mom", _id, "_bc"), params);
    }
    // energy
    if (_model_type == FlowModel::EQ_MODEL_3)
    {
      InputParameters params = _factory.getValidParams("OneDFreeEnergyBC");
      params.set<NonlinearVariableName>("variable") = FlowModel::RHOE;
      params.set<std::vector<unsigned int> >("r7:boundary") = bnd_id;
      params.set<UserObjectName>("eos") = getParam<UserObjectName>("eos");
      // coupling
      params.set<std::vector<VariableName> >("rho") = cv_rho;
      params.set<std::vector<VariableName> >("rhou") = cv_rhou;
      params.set<std::vector<VariableName> >("rhoE") = cv_rhoE;
      params.set<std::vector<VariableName> >("u") = cv_u;
      params.set<std::vector<VariableName> >("enthalpy") = cv_enthalpy;
      params.set<std::vector<VariableName> >("area") = cv_area;
      params.set<PostprocessorName>("c") = c_pps;
      params.set<std::vector<Real> >("scaling_factors") = sf;
      params.set<std::vector<VariableName> >("lambda") = cv_lambda;

      _sim.addBoundaryCondition("OneDFreeEnergyBC", genName("erg", _id, "_bc"), params);
    }
  }

  // add the constraints
  {
    InputParameters params = _factory.getValidParams("FlowConstraint");
    params.set<NonlinearVariableName>("variable") = _lm_name;

    params.set<FlowModel::EModelType>("model_type") = _model_type;
    params.set<std::vector<unsigned int> >("nodes") = _nodes;
    params.set<std::vector<Real> >("normals") = _normals;
    params.set<std::vector<Real> >("K") = _K;
    params.set<UserObjectName>("eos") = getParam<UserObjectName>("eos");
    // coupling
    params.set<std::vector<VariableName> >("u") = cv_u;
    params.set<std::vector<VariableName> >("p") = cv_pressure;
    params.set<std::vector<VariableName> >("rho") = cv_rho;
    params.set<std::vector<VariableName> >("rhou") = cv_rhou;
    params.set<std::vector<VariableName> >("area") = cv_area;
    if (_model_type == FlowModel::EQ_MODEL_3)
    {
      params.set<std::vector<VariableName> >("rhoE") = cv_rhoE;
      params.set<std::vector<VariableName> >("enthalpy") = cv_enthalpy;
    }

    _sim.addScalarKernel("FlowConstraint", genName("flow", _id, "_c0"), params);
  }
}
<|endoftext|>


<commit_before>/* Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.

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

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

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

#include <memory>
#include <string>

#include "paddle/fluid/operators/elementwise/elementwise_div_op.h"
#include "paddle/fluid/platform/device/npu/npu_op_runner.h"

namespace paddle {
namespace operators {

using Tensor = framework::Tensor;

template <typename DeviceContext, typename T>
class ElementwiseDivNPUKernel : public framework::OpKernel<T> {
 public:
  void Compute(const framework::ExecutionContext& ctx) const override {
    auto* x = ctx.Input<Tensor>("X");
    auto* y = ctx.Input<Tensor>("Y");

    auto* out = ctx.Output<Tensor>("Out");

    auto place = ctx.GetPlace();

    out->mutable_data<T>(place);

    auto stream =
        ctx.template device_context<paddle::platform::NPUDeviceContext>()
            .stream();

    const auto& runner = NpuOpRunner("Div", {*x, *y}, {*out}, {});
    runner.Run(stream);
  }
};

template <typename DeviceContext, typename T>
class ElementwiseDivGradNPUKernel : public framework::OpKernel<T> {
 public:
  void Compute(const framework::ExecutionContext& ctx) const override {
    auto* out = ctx.Input<Tensor>("Out");
    auto* dout = ctx.Input<Tensor>(framework::GradVarName("Out"));
    auto* x = ctx.Input<Tensor>("X");
    auto* y = ctx.Input<Tensor>("Y");

    auto* dx = ctx.Output<Tensor>(framework::GradVarName("X"));
    auto* dy = ctx.Output<Tensor>(framework::GradVarName("Y"));

    auto place = ctx.GetPlace();

    auto stream =
        ctx.template device_context<paddle::platform::NPUDeviceContext>()
            .stream();

    if (dx) {
      dx->mutable_data<T>(place);

      Tensor tensor_one(y->type());
      tensor_one.mutable_data<float>({1}, place);
      FillNpuTensorWithConstant<float>(&tensor_one, static_cast<float>(1.0));

      // Use `Div` CANN OP to achieve `1/y` instead of `Power` CANN OP.
      // Because `Power` will cause precision overflow, that is, `float_status`
      // will be set to 1.
      Tensor y_div(y->type());
      y_div.mutable_data<T>(y->dims(), place);
      const auto& runner_one_div_y =
          NpuOpRunner("Div", {tensor_one, *y}, {y_div}, {});
      runner_one_div_y.Run(stream);

      Tensor tensor_zeros(x->type());
      tensor_zeros.mutable_data<T>(x->dims(), place);
      const auto& runner_tensor_zeros =
          NpuOpRunner("ZerosLike", {*x}, {tensor_zeros}, {});
      runner_tensor_zeros.Run(stream);

      Tensor x_zero(experimental::DataType::BOOL);
      x_zero.mutable_data<bool>(x->dims(), place);
      const auto& runner_x_zero =
          NpuOpRunner("Equal", {*x, tensor_zeros}, {x_zero}, {});
      runner_x_zero.Run(stream);

      Tensor x_nozero(experimental::DataType::BOOL);
      x_nozero.mutable_data<bool>(x->dims(), place);
      const auto& runner_x_nonzero =
          NpuOpRunner("LogicalNot", {x_zero}, {x_nozero}, {});
      runner_x_nonzero.Run(stream);

      Tensor x_nozero_f(x->type());
      x_nozero_f.mutable_data<T>(x->dims(), place);
      const auto& runner_x_nonzero_f =
          NpuOpRunner("Cast", {x_nozero}, {x_nozero_f},
                      {{"dst_type", static_cast<int32_t>(0)}});
      runner_x_nonzero_f.Run(stream);

      Tensor x_grad_w(x->type());
      x_grad_w.mutable_data<T>(x->dims(), place);
      const auto& runner_x_grad_w =
          NpuOpRunner("Mul", {x_nozero_f, y_div}, {x_grad_w}, {});
      runner_x_grad_w.Run(stream);

      const auto& runner_x_grad =
          NpuOpRunner("Mul", {x_grad_w, *dout}, {*dx}, {});
      runner_x_grad.Run(stream);
    }

    if (dy) {
      dy->mutable_data<T>(place);

      Tensor neg_out(y->type());
      neg_out.mutable_data<T>(y->dims(), place);
      const auto& runner_neg_out = NpuOpRunner("Neg", {*out}, {neg_out}, {});
      runner_neg_out.Run(stream);

      Tensor y_grad_w(y->type());
      y_grad_w.mutable_data<T>(y->dims(), place);
      const auto& runner_y_grad_w =
          NpuOpRunner("Div", {neg_out, *y}, {y_grad_w}, {});
      runner_y_grad_w.Run(stream);

      const auto& runner_y_grad =
          NpuOpRunner("Mul", {y_grad_w, *dout}, {*dy}, {});
      runner_y_grad.Run(stream);
    }
  }
};

}  // namespace operators
}  // namespace paddle
namespace ops = paddle::operators;

REGISTER_OP_NPU_KERNEL(
    elementwise_div,
    ops::ElementwiseDivNPUKernel<paddle::platform::NPUDeviceContext, float>,
    ops::ElementwiseDivNPUKernel<paddle::platform::NPUDeviceContext,
                                 paddle::platform::float16>);

REGISTER_OP_NPU_KERNEL(
    elementwise_div_grad,
    ops::ElementwiseDivGradNPUKernel<paddle::platform::NPUDeviceContext, float>,
    ops::ElementwiseDivGradNPUKernel<paddle::platform::NPUDeviceContext,
                                     paddle::platform::float16>);
<commit_msg>[NPU] support model PPO (#42484)<commit_after>/* Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.

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

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

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

#include <memory>
#include <string>

#include "paddle/fluid/operators/elementwise/elementwise_div_op.h"
#include "paddle/fluid/platform/device/npu/npu_op_runner.h"

namespace paddle {
namespace operators {

using Tensor = framework::Tensor;

template <typename DeviceContext, typename T>
class ElementwiseDivNPUKernel : public framework::OpKernel<T> {
 public:
  void Compute(const framework::ExecutionContext& ctx) const override {
    auto* x = ctx.Input<Tensor>("X");
    auto* y = ctx.Input<Tensor>("Y");

    auto* out = ctx.Output<Tensor>("Out");

    auto place = ctx.GetPlace();

    out->mutable_data<T>(place);

    auto stream =
        ctx.template device_context<paddle::platform::NPUDeviceContext>()
            .stream();

    const auto& runner = NpuOpRunner("Div", {*x, *y}, {*out}, {});
    runner.Run(stream);
  }
};

template <typename DeviceContext, typename T>
class ElementwiseDivGradNPUKernel : public framework::OpKernel<T> {
 public:
  void Compute(const framework::ExecutionContext& ctx) const override {
    auto* out = ctx.Input<Tensor>("Out");
    auto* dout = ctx.Input<Tensor>(framework::GradVarName("Out"));
    auto* x = ctx.Input<Tensor>("X");
    auto* y = ctx.Input<Tensor>("Y");

    auto* dx = ctx.Output<Tensor>(framework::GradVarName("X"));
    auto* dy = ctx.Output<Tensor>(framework::GradVarName("Y"));

    auto place = ctx.GetPlace();

    auto stream =
        ctx.template device_context<paddle::platform::NPUDeviceContext>()
            .stream();

    if (dx) {
      dx->mutable_data<T>(place);

      Tensor tensor_one(y->type());
      tensor_one.mutable_data<float>({1}, place);
      FillNpuTensorWithConstant<float>(&tensor_one, static_cast<float>(1.0));

      // Use `Div` CANN OP to achieve `1/y` instead of `Power` CANN OP.
      // Because `Power` will cause precision overflow, that is, `float_status`
      // will be set to 1.
      Tensor y_div(y->type());
      y_div.mutable_data<T>(y->dims(), place);
      const auto& runner_one_div_y =
          NpuOpRunner("Div", {tensor_one, *y}, {y_div}, {});
      runner_one_div_y.Run(stream);

      Tensor tensor_zeros(x->type());
      tensor_zeros.mutable_data<T>(x->dims(), place);
      const auto& runner_tensor_zeros =
          NpuOpRunner("ZerosLike", {*x}, {tensor_zeros}, {});
      runner_tensor_zeros.Run(stream);

      Tensor x_zero(experimental::DataType::BOOL);
      x_zero.mutable_data<bool>(x->dims(), place);
      const auto& runner_x_zero =
          NpuOpRunner("Equal", {*x, tensor_zeros}, {x_zero}, {});
      runner_x_zero.Run(stream);

      Tensor x_nozero(experimental::DataType::BOOL);
      x_nozero.mutable_data<bool>(x->dims(), place);
      const auto& runner_x_nonzero =
          NpuOpRunner("LogicalNot", {x_zero}, {x_nozero}, {});
      runner_x_nonzero.Run(stream);

      Tensor x_nozero_f(x->type());
      x_nozero_f.mutable_data<T>(x->dims(), place);
      const auto& runner_x_nonzero_f =
          NpuOpRunner("Cast", {x_nozero}, {x_nozero_f},
                      {{"dst_type", static_cast<int32_t>(0)}});
      runner_x_nonzero_f.Run(stream);

      Tensor x_grad_w(x->type());
      x_grad_w.mutable_data<T>(x->dims(), place);
      const auto& runner_x_grad_w =
          NpuOpRunner("Mul", {x_nozero_f, y_div}, {x_grad_w}, {});
      runner_x_grad_w.Run(stream);

      const auto& runner_x_grad =
          NpuOpRunner("Mul", {x_grad_w, *dout}, {*dx}, {});
      runner_x_grad.Run(stream);
    }

    if (dy) {
      dy->mutable_data<T>(place);

      Tensor neg_out(out->type());
      neg_out.mutable_data<T>(out->dims(), place);
      const auto& runner_neg_out = NpuOpRunner("Neg", {*out}, {neg_out}, {});
      runner_neg_out.Run(stream);

      Tensor tmp_mul(out->type());
      tmp_mul.mutable_data<T>(out->dims(), place);
      const auto& runner_mul =
          NpuOpRunner("Mul", {neg_out, *dout}, {tmp_mul}, {});
      runner_mul.Run(stream);

      if (dy->dims() != dout->dims()) {
        Tensor reduced_tmp_mul(y->type());
        reduced_tmp_mul.mutable_data<T>(y->dims(), place);

        std::vector<int64_t> axes;
        int64_t diff = dout->dims().size() - dy->dims().size();
        for (int64_t i = 0; i < dout->dims().size(); ++i) {
          if (i < diff) {
            axes.push_back(i);
            continue;
          }
          if (dout->dims()[i] > dy->dims()[i - diff]) {
            axes.push_back(i);
          }
        }
        const auto& runner_reduce =
            NpuOpRunner("ReduceSumD", {tmp_mul}, {reduced_tmp_mul},
                        {{"axes", axes}, {"keep_dims", false}});
        runner_reduce.Run(stream);

        const auto& runner_y_grad =
            NpuOpRunner("Div", {reduced_tmp_mul, *y}, {*dy}, {});
        runner_y_grad.Run(stream);
      } else {
        const auto& runner_y_grad =
            NpuOpRunner("Div", {tmp_mul, *y}, {*dy}, {});
        runner_y_grad.Run(stream);
      }
    }
  }
};

}  // namespace operators
}  // namespace paddle
namespace ops = paddle::operators;

REGISTER_OP_NPU_KERNEL(
    elementwise_div,
    ops::ElementwiseDivNPUKernel<paddle::platform::NPUDeviceContext, float>,
    ops::ElementwiseDivNPUKernel<paddle::platform::NPUDeviceContext,
                                 paddle::platform::float16>);

REGISTER_OP_NPU_KERNEL(
    elementwise_div_grad,
    ops::ElementwiseDivGradNPUKernel<paddle::platform::NPUDeviceContext, float>,
    ops::ElementwiseDivGradNPUKernel<paddle::platform::NPUDeviceContext,
                                     paddle::platform::float16>);
<|endoftext|>


<commit_before>/* Copyright 2020 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/python/framework/python_api_dispatcher.h"

#include <set>

#include "absl/strings/str_join.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/python/lib/core/py_util.h"
#include "tensorflow/python/lib/core/safe_pyobject_ptr.h"
#include "tensorflow/python/util/util.h"

namespace tensorflow {
namespace py_dispatch {

namespace {

std::vector<Safe_PyObjectPtr>& GetRegisteredDispatchableTypes() {
  static std::vector<Safe_PyObjectPtr>* registered_dispatchable_types =
      new std::vector<Safe_PyObjectPtr>();
  if (registered_dispatchable_types->empty()) {
    static PyObject* composite_tensor =
        swig::GetRegisteredPyObject("CompositeTensor");
    Py_INCREF(composite_tensor);
    registered_dispatchable_types->push_back(
        Safe_PyObjectPtr(composite_tensor));
  }
  return *registered_dispatchable_types;
}

// Returns true if `py_class` is a registered dispatchable type.
bool IsRegisteredDispatchableType(PyObject* py_class) {
  DCheckPyGilState();
  for (const auto& registered_type : GetRegisteredDispatchableTypes()) {
    int result = PyObject_IsSubclass(py_class, registered_type.get());
    if (result > 0) return true;
    if (result < 0) PyErr_Clear();
  }
  return false;
}

// Raises an exception indicating that multiple dispatch targets matched.
Safe_PyObjectPtr RaiseDispatchConflictError(const std::string& api_name,
                                            PyObject* selected,
                                            PyObject* target) {
  Safe_PyObjectPtr s1(PyObject_Str(selected));
  Safe_PyObjectPtr s2(PyObject_Str(target));
  PyErr_SetString(PyExc_ValueError,
                  absl::StrCat("Multiple dispatch targets that were "
                               "registered with tf.dispatch_for (",
                               s1 ? PyUnicode_AsUTF8(s1.get()) : "?", " and ",
                               s2 ? PyUnicode_AsUTF8(s2.get()) : "?",
                               ") match the arguments to ", api_name)
                      .c_str());
  return nullptr;
}

}  // namespace

bool RegisterDispatchableType(PyObject* py_class) {
  DCheckPyGilState();
  if (!PyType_Check(py_class)) {
    PyErr_SetString(
        PyExc_ValueError,
        absl::StrCat("Expected a type object; got object with type ",
                     py_class->ob_type->tp_name)
            .c_str());
    return false;
  }
  if (IsRegisteredDispatchableType(py_class)) {
    Safe_PyObjectPtr s(PyObject_Str(py_class));
    PyErr_SetString(PyExc_ValueError,
                    absl::StrCat("Type ", s ? PyUnicode_AsUTF8(s.get()) : "?",
                                 " (or one of its bases clases) has "
                                 "already been registered")
                        .c_str());
    return false;
  }
  Py_INCREF(py_class);
  GetRegisteredDispatchableTypes().push_back(Safe_PyObjectPtr(py_class));
  return true;
}

PythonAPIDispatcher::PythonAPIDispatcher(const std::string& api_name,
                                         absl::Span<const char*> arg_names,
                                         absl::Span<PyObject*> defaults)
    : api_name_(api_name),
      canonicalizer_(arg_names, defaults),
      canonicalized_args_storage_(canonicalizer_.GetArgSize()),
      canonicalized_args_span_(canonicalized_args_storage_) {}

void PythonAPIDispatcher::Register(PySignatureChecker signature_checker,
                                   PyObject* dispatch_target) {
  DCheckPyGilState();
  Py_INCREF(dispatch_target);
  targets_.emplace_back(std::move(signature_checker),
                        Safe_PyObjectPtr(dispatch_target));
}

Safe_PyObjectPtr PythonAPIDispatcher::Dispatch(PyObject* args,
                                               PyObject* kwargs) {
  DCheckPyGilState();
  if (kwargs == Py_None) {
    kwargs = nullptr;
  }
  // Canonicalize args (so we don't need to deal with kwargs).
  if (!canonicalizer_.Canonicalize(args, kwargs, canonicalized_args_span_)) {
    return nullptr;
  }

  PyObject* selected = nullptr;
  for (auto& target : targets_) {
    if (target.first.CheckCanonicalizedArgs(canonicalized_args_span_)) {
      if (selected && selected != target.second.get()) {
        return RaiseDispatchConflictError(api_name_, selected,
                                          target.second.get());
      }
      selected = target.second.get();
    }
  }
  if (selected) {
    return Safe_PyObjectPtr(PyObject_Call(selected, args, kwargs));
  } else {
    Py_INCREF(Py_NotImplemented);
    return Safe_PyObjectPtr(Py_NotImplemented);
  }
}

// TODO(b/194903203) Raise an error if `func` is not registered.
void PythonAPIDispatcher::Unregister(PyObject* func) {
  DCheckPyGilState();
  using DispatchTargetPair = std::pair<PySignatureChecker, Safe_PyObjectPtr>;
  targets_.erase(std::remove_if(targets_.begin(), targets_.end(),
                                [func](const DispatchTargetPair& t) {
                                  return t.second.get() == func;
                                }),
                 targets_.end());
}

std::string PythonAPIDispatcher::DebugString() const {
  DCheckPyGilState();
  std::string out = absl::StrCat("<Disptach(", api_name_, "): ");

  const char* sep = "";
  for (const auto& target : targets_) {
    Safe_PyObjectPtr target_str(PyObject_Str(target.second.get()));
    absl::StrAppend(&out, sep, target.first.DebugString(), " -> ",
                    target_str ? PyUnicode_AsUTF8(target_str.get()) : "?");
    sep = ", ";
  }
  return out;
}

PySignatureChecker::PySignatureChecker(
    std::vector<ParamChecker> parameter_checkers)
    : positional_parameter_checkers_(std::move(parameter_checkers)) {
  // Check less expensive parameters first.
  std::sort(positional_parameter_checkers_.begin(),
            positional_parameter_checkers_.end(),
            [](ParamChecker a, ParamChecker b) {
              return a.second->cost() < b.second->cost();
            });
}

bool PySignatureChecker::CheckCanonicalizedArgs(
    absl::Span<PyObject*> canon_args) const {
  bool matched_dispatchable_type = false;
  for (auto& c : positional_parameter_checkers_) {
    int index = c.first;
    auto& param_checker = c.second;
    if (index >= canon_args.size()) {
      return false;
    }
    switch (param_checker->Check(canon_args[index])) {
      case PyTypeChecker::MatchType::NO_MATCH:
        return false;
      case PyTypeChecker::MatchType::MATCH_DISPATCHABLE:
        matched_dispatchable_type = true;
        break;
      case PyTypeChecker::MatchType::MATCH:
        break;
    }
  }
  return matched_dispatchable_type;
}

std::string PySignatureChecker::DebugString() const {
  return absl::StrJoin(positional_parameter_checkers_, ", ",
                       [](std::string* out, ParamChecker p) {
                         absl::StrAppend(out, "args[", p.first,
                                         "]:", p.second->DebugString());
                       });
}

PyInstanceChecker::PyInstanceChecker(const std::vector<PyObject*>& py_classes) {
  DCheckPyGilState();
  py_classes_.reserve(py_classes.size());
  for (PyObject* py_class : py_classes) {
    py_classes_.emplace_back(py_class);
    Py_INCREF(py_class);
  }
}

PyInstanceChecker::~PyInstanceChecker() {
  DCheckPyGilState();
  for (const auto& pair : py_class_cache_) {
    Py_DECREF(pair.first);
  }
}

PyTypeChecker::MatchType PyInstanceChecker::Check(PyObject* value) {
  DCheckPyGilState();
  auto* type = Py_TYPE(value);
  auto it = py_class_cache_.find(type);
  if (it != py_class_cache_.end()) {
    return it->second;
  }

  MatchType result = MatchType::NO_MATCH;
  for (const auto& py_class : py_classes_) {
    int is_instance = PyObject_IsInstance(value, py_class.get());
    if (is_instance == 1) {
      if (IsRegisteredDispatchableType(py_class.get())) {
        result = MatchType::MATCH_DISPATCHABLE;
        break;
      } else {
        result = MatchType::MATCH;
      }
    } else if (is_instance < 0) {
      PyErr_Clear();
      return MatchType::NO_MATCH;
    }
  }

  if (py_class_cache_.size() < kMaxItemsInCache) {
    Py_INCREF(type);
    auto insert_result = py_class_cache_.insert({type, result});
    DCHECK(insert_result.second);
  }
  return result;
}

int PyInstanceChecker::cost() const { return py_classes_.size(); }

std::string PyInstanceChecker::DebugString() const {
  DCheckPyGilState();
  std::vector<const char*> type_names;
  for (const auto& py_class : py_classes_) {
    type_names.push_back(
        reinterpret_cast<PyTypeObject*>(py_class.get())->tp_name);
  }
  return absl::StrJoin(
      py_classes_, ", ", [](std::string* out, const Safe_PyObjectPtr& v) {
        out->append(reinterpret_cast<PyTypeObject*>(v.get())->tp_name);
      });
}

PyTypeChecker::MatchType PyListChecker::Check(PyObject* value) {
  DCheckPyGilState();
  if (!(PyList_Check(value) || PyTuple_Check(value))) {
    return MatchType::NO_MATCH;
  }

  Safe_PyObjectPtr seq(PySequence_Fast(value, ""));
  if (!seq) {
    PyErr_Clear();
    return MatchType::NO_MATCH;  // value is not a sequence.
  }

  MatchType result = MatchType::MATCH;
  for (int i = 0; i < PySequence_Fast_GET_SIZE(seq.get()); ++i) {
    switch (element_type_->Check(PySequence_Fast_GET_ITEM(seq.get(), i))) {
      case MatchType::NO_MATCH:
        return MatchType::NO_MATCH;
      case MatchType::MATCH_DISPATCHABLE:
        result = MatchType::MATCH_DISPATCHABLE;
        break;
      case MatchType::MATCH:
        break;
    }
  }
  return result;
}

int PyListChecker::cost() const { return 10 * element_type_->cost(); }

std::string PyListChecker::DebugString() const {
  return absl::StrCat("List[", element_type_->DebugString(), "]");
}

PyTypeChecker::MatchType PyUnionChecker::Check(PyObject* value) {
  MatchType result = MatchType::NO_MATCH;
  for (auto& type_option : options_) {
    switch (type_option->Check(value)) {
      case MatchType::MATCH:
        result = MatchType::MATCH;
        break;
      case MatchType::MATCH_DISPATCHABLE:
        return MatchType::MATCH_DISPATCHABLE;
      case MatchType::NO_MATCH:
        break;
    }
  }
  return result;
}

int PyUnionChecker::cost() const {
  int cost = 1;
  for (auto& type_option : options_) {
    cost += type_option->cost();
  }
  return cost;
}

std::string PyUnionChecker::DebugString() const {
  return absl::StrCat("Union[",
                      absl::StrJoin(options_, ", ",
                                    [](std::string* out, PyTypeChecker_ptr v) {
                                      out->append(v->DebugString());
                                    }),
                      "]");
}

}  // namespace py_dispatch
}  // namespace tensorflow
<commit_msg>Avoid updating PyInstanceChecker cache when it's not necessary.<commit_after>/* Copyright 2020 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/python/framework/python_api_dispatcher.h"

#include <set>

#include "absl/strings/str_join.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/python/lib/core/py_util.h"
#include "tensorflow/python/lib/core/safe_pyobject_ptr.h"
#include "tensorflow/python/util/util.h"

namespace tensorflow {
namespace py_dispatch {

namespace {

std::vector<Safe_PyObjectPtr>& GetRegisteredDispatchableTypes() {
  static std::vector<Safe_PyObjectPtr>* registered_dispatchable_types =
      new std::vector<Safe_PyObjectPtr>();
  if (registered_dispatchable_types->empty()) {
    static PyObject* composite_tensor =
        swig::GetRegisteredPyObject("CompositeTensor");
    Py_INCREF(composite_tensor);
    registered_dispatchable_types->push_back(
        Safe_PyObjectPtr(composite_tensor));
  }
  return *registered_dispatchable_types;
}

// Returns true if `py_class` is a registered dispatchable type.
bool IsRegisteredDispatchableType(PyObject* py_class) {
  DCheckPyGilState();
  for (const auto& registered_type : GetRegisteredDispatchableTypes()) {
    int result = PyObject_IsSubclass(py_class, registered_type.get());
    if (result > 0) return true;
    if (result < 0) PyErr_Clear();
  }
  return false;
}

// Raises an exception indicating that multiple dispatch targets matched.
Safe_PyObjectPtr RaiseDispatchConflictError(const std::string& api_name,
                                            PyObject* selected,
                                            PyObject* target) {
  Safe_PyObjectPtr s1(PyObject_Str(selected));
  Safe_PyObjectPtr s2(PyObject_Str(target));
  PyErr_SetString(PyExc_ValueError,
                  absl::StrCat("Multiple dispatch targets that were "
                               "registered with tf.dispatch_for (",
                               s1 ? PyUnicode_AsUTF8(s1.get()) : "?", " and ",
                               s2 ? PyUnicode_AsUTF8(s2.get()) : "?",
                               ") match the arguments to ", api_name)
                      .c_str());
  return nullptr;
}

}  // namespace

bool RegisterDispatchableType(PyObject* py_class) {
  DCheckPyGilState();
  if (!PyType_Check(py_class)) {
    PyErr_SetString(
        PyExc_ValueError,
        absl::StrCat("Expected a type object; got object with type ",
                     py_class->ob_type->tp_name)
            .c_str());
    return false;
  }
  if (IsRegisteredDispatchableType(py_class)) {
    Safe_PyObjectPtr s(PyObject_Str(py_class));
    PyErr_SetString(PyExc_ValueError,
                    absl::StrCat("Type ", s ? PyUnicode_AsUTF8(s.get()) : "?",
                                 " (or one of its bases clases) has "
                                 "already been registered")
                        .c_str());
    return false;
  }
  Py_INCREF(py_class);
  GetRegisteredDispatchableTypes().push_back(Safe_PyObjectPtr(py_class));
  return true;
}

PythonAPIDispatcher::PythonAPIDispatcher(const std::string& api_name,
                                         absl::Span<const char*> arg_names,
                                         absl::Span<PyObject*> defaults)
    : api_name_(api_name),
      canonicalizer_(arg_names, defaults),
      canonicalized_args_storage_(canonicalizer_.GetArgSize()),
      canonicalized_args_span_(canonicalized_args_storage_) {}

void PythonAPIDispatcher::Register(PySignatureChecker signature_checker,
                                   PyObject* dispatch_target) {
  DCheckPyGilState();
  Py_INCREF(dispatch_target);
  targets_.emplace_back(std::move(signature_checker),
                        Safe_PyObjectPtr(dispatch_target));
}

Safe_PyObjectPtr PythonAPIDispatcher::Dispatch(PyObject* args,
                                               PyObject* kwargs) {
  DCheckPyGilState();
  if (kwargs == Py_None) {
    kwargs = nullptr;
  }
  // Canonicalize args (so we don't need to deal with kwargs).
  if (!canonicalizer_.Canonicalize(args, kwargs, canonicalized_args_span_)) {
    return nullptr;
  }

  PyObject* selected = nullptr;
  for (auto& target : targets_) {
    if (target.first.CheckCanonicalizedArgs(canonicalized_args_span_)) {
      if (selected && selected != target.second.get()) {
        return RaiseDispatchConflictError(api_name_, selected,
                                          target.second.get());
      }
      selected = target.second.get();
    }
  }
  if (selected) {
    return Safe_PyObjectPtr(PyObject_Call(selected, args, kwargs));
  } else {
    Py_INCREF(Py_NotImplemented);
    return Safe_PyObjectPtr(Py_NotImplemented);
  }
}

// TODO(b/194903203) Raise an error if `func` is not registered.
void PythonAPIDispatcher::Unregister(PyObject* func) {
  DCheckPyGilState();
  using DispatchTargetPair = std::pair<PySignatureChecker, Safe_PyObjectPtr>;
  targets_.erase(std::remove_if(targets_.begin(), targets_.end(),
                                [func](const DispatchTargetPair& t) {
                                  return t.second.get() == func;
                                }),
                 targets_.end());
}

std::string PythonAPIDispatcher::DebugString() const {
  DCheckPyGilState();
  std::string out = absl::StrCat("<Disptach(", api_name_, "): ");

  const char* sep = "";
  for (const auto& target : targets_) {
    Safe_PyObjectPtr target_str(PyObject_Str(target.second.get()));
    absl::StrAppend(&out, sep, target.first.DebugString(), " -> ",
                    target_str ? PyUnicode_AsUTF8(target_str.get()) : "?");
    sep = ", ";
  }
  return out;
}

PySignatureChecker::PySignatureChecker(
    std::vector<ParamChecker> parameter_checkers)
    : positional_parameter_checkers_(std::move(parameter_checkers)) {
  // Check less expensive parameters first.
  std::sort(positional_parameter_checkers_.begin(),
            positional_parameter_checkers_.end(),
            [](ParamChecker a, ParamChecker b) {
              return a.second->cost() < b.second->cost();
            });
}

bool PySignatureChecker::CheckCanonicalizedArgs(
    absl::Span<PyObject*> canon_args) const {
  bool matched_dispatchable_type = false;
  for (auto& c : positional_parameter_checkers_) {
    int index = c.first;
    auto& param_checker = c.second;
    if (index >= canon_args.size()) {
      return false;
    }
    switch (param_checker->Check(canon_args[index])) {
      case PyTypeChecker::MatchType::NO_MATCH:
        return false;
      case PyTypeChecker::MatchType::MATCH_DISPATCHABLE:
        matched_dispatchable_type = true;
        break;
      case PyTypeChecker::MatchType::MATCH:
        break;
    }
  }
  return matched_dispatchable_type;
}

std::string PySignatureChecker::DebugString() const {
  return absl::StrJoin(positional_parameter_checkers_, ", ",
                       [](std::string* out, ParamChecker p) {
                         absl::StrAppend(out, "args[", p.first,
                                         "]:", p.second->DebugString());
                       });
}

PyInstanceChecker::PyInstanceChecker(const std::vector<PyObject*>& py_classes) {
  DCheckPyGilState();
  py_classes_.reserve(py_classes.size());
  for (PyObject* py_class : py_classes) {
    py_classes_.emplace_back(py_class);
    Py_INCREF(py_class);
  }
}

PyInstanceChecker::~PyInstanceChecker() {
  DCheckPyGilState();
  for (const auto& pair : py_class_cache_) {
    Py_DECREF(pair.first);
  }
}

PyTypeChecker::MatchType PyInstanceChecker::Check(PyObject* value) {
  DCheckPyGilState();
  auto* type = Py_TYPE(value);
  auto it = py_class_cache_.find(type);
  if (it != py_class_cache_.end()) {
    return it->second;
  }

  MatchType result = MatchType::NO_MATCH;
  for (const auto& py_class : py_classes_) {
    int is_instance = PyObject_IsInstance(value, py_class.get());
    if (is_instance == 1) {
      if (IsRegisteredDispatchableType(py_class.get())) {
        result = MatchType::MATCH_DISPATCHABLE;
        break;
      } else {
        result = MatchType::MATCH;
      }
    } else if (is_instance < 0) {
      PyErr_Clear();
      return MatchType::NO_MATCH;
    }
  }

  if (py_class_cache_.size() < kMaxItemsInCache) {
    Py_INCREF(type);
    auto insert_result = py_class_cache_.insert({type, result});
    if (!insert_result.second) {
      Py_DECREF(type);  // Result was added by a different thread.
    }
  }
  return result;
}

int PyInstanceChecker::cost() const { return py_classes_.size(); }

std::string PyInstanceChecker::DebugString() const {
  DCheckPyGilState();
  std::vector<const char*> type_names;
  for (const auto& py_class : py_classes_) {
    type_names.push_back(
        reinterpret_cast<PyTypeObject*>(py_class.get())->tp_name);
  }
  return absl::StrJoin(
      py_classes_, ", ", [](std::string* out, const Safe_PyObjectPtr& v) {
        out->append(reinterpret_cast<PyTypeObject*>(v.get())->tp_name);
      });
}

PyTypeChecker::MatchType PyListChecker::Check(PyObject* value) {
  DCheckPyGilState();
  if (!(PyList_Check(value) || PyTuple_Check(value))) {
    return MatchType::NO_MATCH;
  }

  Safe_PyObjectPtr seq(PySequence_Fast(value, ""));
  if (!seq) {
    PyErr_Clear();
    return MatchType::NO_MATCH;  // value is not a sequence.
  }

  MatchType result = MatchType::MATCH;
  for (int i = 0; i < PySequence_Fast_GET_SIZE(seq.get()); ++i) {
    switch (element_type_->Check(PySequence_Fast_GET_ITEM(seq.get(), i))) {
      case MatchType::NO_MATCH:
        return MatchType::NO_MATCH;
      case MatchType::MATCH_DISPATCHABLE:
        result = MatchType::MATCH_DISPATCHABLE;
        break;
      case MatchType::MATCH:
        break;
    }
  }
  return result;
}

int PyListChecker::cost() const { return 10 * element_type_->cost(); }

std::string PyListChecker::DebugString() const {
  return absl::StrCat("List[", element_type_->DebugString(), "]");
}

PyTypeChecker::MatchType PyUnionChecker::Check(PyObject* value) {
  MatchType result = MatchType::NO_MATCH;
  for (auto& type_option : options_) {
    switch (type_option->Check(value)) {
      case MatchType::MATCH:
        result = MatchType::MATCH;
        break;
      case MatchType::MATCH_DISPATCHABLE:
        return MatchType::MATCH_DISPATCHABLE;
      case MatchType::NO_MATCH:
        break;
    }
  }
  return result;
}

int PyUnionChecker::cost() const {
  int cost = 1;
  for (auto& type_option : options_) {
    cost += type_option->cost();
  }
  return cost;
}

std::string PyUnionChecker::DebugString() const {
  return absl::StrCat("Union[",
                      absl::StrJoin(options_, ", ",
                                    [](std::string* out, PyTypeChecker_ptr v) {
                                      out->append(v->DebugString());
                                    }),
                      "]");
}

}  // namespace py_dispatch
}  // namespace tensorflow
<|endoftext|>