<commit_before>#include <stdio.h>

#include <boost/format.hpp>

#include "common.h"

#include "dense_matrix.h"
#include "mem_matrix_store.h"
#include "mem_worker_thread.h"
#include "local_matrix_store.h"
#include "sparse_matrix.h"

using namespace fm;

int num_nodes = -1;

class set_col_operate: public type_set_operate<double>
{
	size_t num_cols;
public:
	set_col_operate(size_t num_cols) {
		this->num_cols = num_cols;
	}

	void set(double *arr, size_t num_eles, off_t row_idx, off_t col_idx) const {
		for (size_t i = 0; i < num_eles; i++) {
			arr[i] = (row_idx + i) * num_cols + col_idx;
		}
	}
};

class set_row_operate: public type_set_operate<double>
{
	size_t num_cols;
public:
	set_row_operate(size_t num_cols) {
		this->num_cols = num_cols;
	}

	void set(double *arr, size_t num_eles, off_t row_idx, off_t col_idx) const {
		for (size_t i = 0; i < num_eles; i++) {
			arr[i] = row_idx * num_cols + col_idx + i;
		}
	}
};

class init_vec: public type_set_vec_operate<double>
{
public:
	virtual void set(double *arr, size_t num_eles, off_t start_idx) const {
		for (size_t i = 0; i < num_eles; i++)
			arr[i] = start_idx + i;
	}
};

void test_MM_blas_chain(size_t nrow, size_t ncol, bool in_mem = true)
{
	printf("multiply matrix: M(%ld x %ld) * M(%ld %ld) twice\n",
			nrow, ncol, ncol, ncol);
	dense_matrix::ptr m1
		= dense_matrix::create(nrow, ncol, matrix_layout_t::L_COL,
				get_scalar_type<double>(), set_col_operate(ncol), num_nodes,
				in_mem);
	dense_matrix::ptr m2
		= dense_matrix::create(ncol, ncol, matrix_layout_t::L_COL,
				get_scalar_type<double>(), set_col_operate(ncol), num_nodes,
				in_mem);

	struct timeval start, end;
	{
		gettimeofday(&start, NULL);
		dense_matrix::ptr res1 = m1->multiply(*m2, matrix_layout_t::L_NONE, true);
		res1->materialize_self();
		gettimeofday(&end, NULL);
		printf("It takes %.3f seconds to multiply column matrix in parallel\n",
				time_diff(start, end));
	}

	printf("multiply two small matrices in a row\n");
	{
		gettimeofday(&start, NULL);
		dense_matrix::ptr res1 = m1->multiply(*m2, matrix_layout_t::L_NONE,
				true);
		dense_matrix::ptr res2 = res1->multiply(*m2, matrix_layout_t::L_NONE,
				true);
		res2->materialize_self();
		gettimeofday(&end, NULL);
		printf("It takes %.3f seconds to multiply column matrix in parallel\n",
				time_diff(start, end));
	}

	printf("multiply three small matrices in a row\n");
	{
		gettimeofday(&start, NULL);
		dense_matrix::ptr res1 = m1->multiply(*m2, matrix_layout_t::L_NONE,
				true);
		dense_matrix::ptr res2 = res1->multiply(*m2, matrix_layout_t::L_NONE, true);
		dense_matrix::ptr res3 = res2->multiply(*m2, matrix_layout_t::L_NONE, true);
		res3->materialize_self();
		gettimeofday(&end, NULL);
		printf("It takes %.3f seconds to multiply column matrix in parallel\n",
				time_diff(start, end));
	}

	printf("multiply four small matrices in a row\n");
	{
		gettimeofday(&start, NULL);
		dense_matrix::ptr res1 = m1->multiply(*m2, matrix_layout_t::L_NONE,
				true);
		dense_matrix::ptr res2 = res1->multiply(*m2, matrix_layout_t::L_NONE, true);
		dense_matrix::ptr res3 = res2->multiply(*m2, matrix_layout_t::L_NONE, true);
		dense_matrix::ptr res4 = res3->multiply(*m2, matrix_layout_t::L_NONE, true);
		res4->materialize_self();
		gettimeofday(&end, NULL);
		printf("It takes %.3f seconds to multiply column matrix in parallel\n",
				time_diff(start, end));
	}

	printf("multiply five small matrices in a row\n");
	{
		gettimeofday(&start, NULL);
		dense_matrix::ptr res1 = m1->multiply(*m2, matrix_layout_t::L_NONE,
				true);
		dense_matrix::ptr res2 = res1->multiply(*m2, matrix_layout_t::L_NONE, true);
		dense_matrix::ptr res3 = res2->multiply(*m2, matrix_layout_t::L_NONE, true);
		dense_matrix::ptr res4 = res3->multiply(*m2, matrix_layout_t::L_NONE, true);
		dense_matrix::ptr res5 = res4->multiply(*m2, matrix_layout_t::L_NONE, true);
		res5->materialize_self();
		gettimeofday(&end, NULL);
		printf("It takes %.3f seconds to multiply column matrix in parallel\n",
				time_diff(start, end));
	}
}

dense_matrix::ptr test_MM_blas(size_t nrow, size_t ncol, size_t right_ncol,
		bool in_mem = true)
{
	printf("multiply matrix: M(%ld x %ld) * M(%ld %ld)\n",
			nrow, ncol, ncol, right_ncol);

	struct timeval start, end;
	gettimeofday(&start, NULL);
	dense_matrix::ptr m1
		= dense_matrix::create(nrow, ncol, matrix_layout_t::L_COL,
				get_scalar_type<double>(), set_col_operate(ncol), num_nodes,
				in_mem);
	gettimeofday(&end, NULL);
	printf("It takes %.3f seconds to construct input column matrix\n",
			time_diff(start, end));
	dense_matrix::ptr m2
		= dense_matrix::create(ncol, right_ncol, matrix_layout_t::L_COL,
				get_scalar_type<double>(), set_col_operate(right_ncol), num_nodes,
				in_mem);

	gettimeofday(&start, NULL);
	dense_matrix::ptr res1 = m1->multiply(*m2, matrix_layout_t::L_NONE, true);
	res1->materialize_self();
	gettimeofday(&end, NULL);
	printf("It takes %.3f seconds to multiply column matrix in parallel\n",
			time_diff(start, end));
	return res1;
}

dense_matrix::ptr test_scale_cols(size_t nrow, size_t ncol, bool in_mem = true)
{
	printf("scale cols: M(%ld x %ld)\n", nrow, ncol);

	struct timeval start, end;
	dense_matrix::ptr m1
		= dense_matrix::create(nrow, ncol, matrix_layout_t::L_COL,
				get_scalar_type<double>(), set_col_operate(ncol), num_nodes,
				in_mem);
	vector::ptr v = vector::create(ncol, get_scalar_type<double>(), -1, true,
			init_vec());

	gettimeofday(&start, NULL);
	dense_matrix::ptr res1 = m1->scale_cols(v);
	res1->materialize_self();
	gettimeofday(&end, NULL);
	printf("It takes %.3f seconds to scale cols\n", time_diff(start, end));
	return res1;
}

dense_matrix::ptr test_add(size_t nrow, size_t ncol, bool in_mem = true)
{
	printf("add matrix: M(%ld x %ld)\n", nrow, ncol);

	struct timeval start, end;
	dense_matrix::ptr m1
		= dense_matrix::create(nrow, ncol, matrix_layout_t::L_COL,
				get_scalar_type<double>(), set_col_operate(ncol), num_nodes,
				in_mem);
	dense_matrix::ptr m2
		= dense_matrix::create(nrow, ncol, matrix_layout_t::L_COL,
				get_scalar_type<double>(), set_col_operate(ncol), num_nodes,
				in_mem);

	gettimeofday(&start, NULL);
	dense_matrix::ptr res1 = m1->add(*m2);
	res1->materialize_self();
	gettimeofday(&end, NULL);
	printf("It takes %.3f seconds to add matrix\n", time_diff(start, end));
	return res1;
}

dense_matrix::ptr test_scale_mat(size_t nrow, size_t ncol, bool in_mem = true)
{
	printf("scale matrix: M(%ld x %ld)\n", nrow, ncol);

	struct timeval start, end;
	dense_matrix::ptr m1
		= dense_matrix::create(nrow, ncol, matrix_layout_t::L_COL,
				get_scalar_type<double>(), set_col_operate(ncol), num_nodes,
				in_mem);

	gettimeofday(&start, NULL);
	dense_matrix::ptr res1 = m1->multiply_scalar<double>(2);
	res1->materialize_self();
	gettimeofday(&end, NULL);
	printf("It takes %.3f seconds to scale matrix\n", time_diff(start, end));
	return res1;
}

void test_norm(size_t nrow, size_t ncol, bool in_mem = true)
{
	printf("norm: M(%ld x %ld)\n", nrow, ncol);

	struct timeval start, end;
	dense_matrix::ptr m1
		= dense_matrix::create(nrow, ncol, matrix_layout_t::L_COL,
				get_scalar_type<double>(), set_col_operate(ncol), num_nodes,
				in_mem);

	gettimeofday(&start, NULL);
	m1->col_norm2();
	gettimeofday(&end, NULL);
	printf("It takes %.3f seconds to norm cols\n", time_diff(start, end));
}

void matrix_mul_tests(size_t long_dim)
{
	for (size_t short_dim = 4; short_dim <= 64; short_dim *= 2)
		test_MM_blas(short_dim, long_dim, short_dim);
	for (size_t short_dim = 4; short_dim <= 64; short_dim *= 2)
		test_MM_blas(long_dim, short_dim, short_dim);
	for (size_t short_dim = 4; short_dim <= 64; short_dim *= 2)
		test_MM_blas(short_dim, long_dim, short_dim, false);
	for (size_t short_dim = 4; short_dim <= 64; short_dim *= 2)
		test_MM_blas(long_dim, short_dim, short_dim, false);

	size_t short_dim = 16;
	test_MM_blas_chain(long_dim, short_dim);
	test_MM_blas_chain(long_dim, short_dim, false);
}

int main(int argc, char *argv[])
{
	if (argc < 2) {
		fprintf(stderr, "test conf_file\n");
		exit(1);
	}

	std::string conf_file = argv[1];
	config_map::ptr configs = config_map::create(conf_file);
	init_flash_matrix(configs);
	if (matrix_conf.get_num_nodes() > 1)
		num_nodes = matrix_conf.get_num_nodes();

	size_t long_dim = 1024 * 1024 * 200;
	size_t short_dim = 16;
	matrix_mul_tests(long_dim);
	test_norm(long_dim, short_dim);
	test_scale_mat(long_dim, short_dim);
	test_add(long_dim, short_dim);
	test_scale_cols(long_dim, short_dim);
	test_norm(long_dim, short_dim, false);
	test_scale_mat(long_dim, short_dim, false);
	test_add(long_dim, short_dim, false);
	test_scale_cols(long_dim, short_dim, false);

	destroy_flash_matrix();
}
<commit_msg>[Matrix]: delete perf test on dense matrices.<commit_after><|endoftext|>


<commit_before>// For conditions of distribution and use, see copyright notice in license.txt

#include "StableHeaders.h"
#include "DebugOperatorNew.h"
#include "EC_ParticleSystem.h"
#include "IModule.h"
#include "Entity.h"
#include "Renderer.h"
#include "EC_Placeable.h"
#include "OgreParticleAsset.h"
#include "OgreConversionUtils.h"
#include "SceneManager.h"
#include "AssetAPI.h"
#include "IAssetTransfer.h"
#include "LoggingFunctions.h"

DEFINE_POCO_LOGGING_FUNCTIONS("EC_ParticleSystem")

#include <Ogre.h>
#include "MemoryLeakCheck.h"

using namespace OgreRenderer;

EC_ParticleSystem::EC_ParticleSystem(IModule *module):
    IComponent(module->GetFramework()),
    particleRef(this, "Particle ref" ),
    castShadows(this, "Cast shadows", false),
    renderingDistance(this, "Rendering distance", 0.0f)
{
    renderer_ = GetFramework()->GetServiceManager()->GetService<Renderer>();
    connect(this, SIGNAL(ParentEntitySet()), this, SLOT(EntitySet()));
    connect(this, SIGNAL(OnAttributeChanged(IAttribute*, AttributeChange::Type)), this, SLOT(AttributeUpdated(IAttribute*)));
}

EC_ParticleSystem::~EC_ParticleSystem()
{
    DeleteParticleSystems();
}

void EC_ParticleSystem::CreateParticleSystem(const QString &systemName)
{
    if (!ViewEnabled())
        return;
    if (renderer_.expired())
        return;
    RendererPtr renderer = renderer_.lock();

    try
    {
        EC_Placeable *placeable = dynamic_cast<EC_Placeable *>(FindPlaceable().get());
        if (!placeable)
        {
            LogError("Fail to create a new particle system, make sure that entity has EC_Placeable component created.");
            return;
        }

        Ogre::SceneManager* scene_mgr = renderer->GetSceneManager();
        Ogre::ParticleSystem* system = scene_mgr->createParticleSystem(renderer->GetUniqueObjectName("EC_Particlesystem"), SanitateAssetIdForOgre(systemName.toStdString()));
        if (system)
        {
            placeable->GetSceneNode()->attachObject(system);
            particleSystems_.push_back(system);
            system->setCastShadows(castShadows.Get());
            system->setRenderingDistance(renderingDistance.Get());
            return;
        }
    }
    catch (Ogre::Exception& e)
    {
        LogError("Could not add particle system " + Name().toStdString() + ": " + std::string(e.what()));
    }

    return;
}

void EC_ParticleSystem::DeleteParticleSystems()
{
    if (renderer_.expired())
        return;
    RendererPtr renderer = renderer_.lock();

    Ogre::SceneManager* scene_mgr = renderer->GetSceneManager();
    if (!scene_mgr)
        return;

    try
    {
        EC_Placeable *placeable = dynamic_cast<EC_Placeable *>(FindPlaceable().get());
        if (placeable)
        {
            Ogre::SceneNode *node = placeable->GetSceneNode();
            if (!node)
                return;
            for (unsigned i = 0; i < particleSystems_.size(); ++i)
                node->detachObject(particleSystems_[i]);
        }
        for (unsigned i = 0; i < particleSystems_.size(); ++i)
            scene_mgr->destroyParticleSystem(particleSystems_[i]);
    }
    catch (Ogre::Exception& e)
    {
        LogError("Could not delete particle systems");
    }

    particleSystems_.clear();

    return;
}

void EC_ParticleSystem::AttributeUpdated(IAttribute *attribute)
{
    if(attribute == &castShadows)
    {
        for (unsigned i = 0; i < particleSystems_.size(); ++i)
            particleSystems_[i]->setCastShadows(castShadows.Get());
    }
    else if (attribute == &renderingDistance)
    {
        for (unsigned i = 0; i < particleSystems_.size(); ++i)
            particleSystems_[i]->setRenderingDistance(renderingDistance.Get());
    }
    else if (attribute == &particleRef)
    {
        if (!ViewEnabled())
            return;

        // Request the new particle system resource. Once it has loaded, ParticleSystemAssetLoaded() will be called.
        AssetTransferPtr transfer = GetFramework()->Asset()->RequestAsset(particleRef.Get());
        if ( transfer.get() != 0)
        {
            connect(transfer.get(), SIGNAL(Loaded(IAssetTransfer*)), SLOT(ParticleSystemAssetLoaded()), Qt::UniqueConnection);
        }
        else
        {
            DeleteParticleSystems();
        }
    }
}

ComponentPtr EC_ParticleSystem::FindPlaceable() const
{
    if (GetParentEntity())
        return GetParentEntity()->GetComponent<EC_Placeable>();
    else
        return ComponentPtr();
}

void EC_ParticleSystem::ParticleSystemAssetLoaded()
{
    IAssetTransfer *transfer = dynamic_cast<IAssetTransfer*>(sender());
    assert(transfer);
    if (!transfer)
        return;

    OgreParticleAsset *asset = dynamic_cast<OgreParticleAsset*>(transfer->asset.get());
    if (!asset)
    {
        LogWarning("EC_ParticleSystem::ParticleSystemAssetLoaded: Asset transfer finished, but asset pointer was null!");
        return;
    }

    if (asset->GetNumTemplates() > 0)
    {
        DeleteParticleSystems();
        for (int i = 0 ; i< asset->GetNumTemplates(); ++i)
            CreateParticleSystem(asset->GetTemplateName(i));
    }
}

void EC_ParticleSystem::EntitySet()
{
    Scene::Entity *entity = this->GetParentEntity();
    if (!entity)
    {
        LogError("Failed to connect entity signals, component's parent entity is null");
        return;
    }

    QObject::connect(entity, SIGNAL(ComponentRemoved(IComponent*, AttributeChange::Type)), this, SLOT(OnComponentRemoved(IComponent*, AttributeChange::Type)), Qt::UniqueConnection);
}

void EC_ParticleSystem::OnComponentRemoved(IComponent *component, AttributeChange::Type change)
{
    // If the component is the Placeable, delete particle systems now for safety
    if (component->TypeName() == EC_Placeable::TypeNameStatic())
        DeleteParticleSystems();
}
<commit_msg>Fix unreferenced param warning.<commit_after>// For conditions of distribution and use, see copyright notice in license.txt

#include "StableHeaders.h"
#include "DebugOperatorNew.h"
#include "EC_ParticleSystem.h"
#include "IModule.h"
#include "Entity.h"
#include "Renderer.h"
#include "EC_Placeable.h"
#include "OgreParticleAsset.h"
#include "OgreConversionUtils.h"
#include "SceneManager.h"
#include "AssetAPI.h"
#include "IAssetTransfer.h"
#include "LoggingFunctions.h"

DEFINE_POCO_LOGGING_FUNCTIONS("EC_ParticleSystem")

#include <Ogre.h>
#include "MemoryLeakCheck.h"

using namespace OgreRenderer;

EC_ParticleSystem::EC_ParticleSystem(IModule *module):
    IComponent(module->GetFramework()),
    particleRef(this, "Particle ref" ),
    castShadows(this, "Cast shadows", false),
    renderingDistance(this, "Rendering distance", 0.0f)
{
    renderer_ = GetFramework()->GetServiceManager()->GetService<Renderer>();
    connect(this, SIGNAL(ParentEntitySet()), this, SLOT(EntitySet()));
    connect(this, SIGNAL(OnAttributeChanged(IAttribute*, AttributeChange::Type)), this, SLOT(AttributeUpdated(IAttribute*)));
}

EC_ParticleSystem::~EC_ParticleSystem()
{
    DeleteParticleSystems();
}

void EC_ParticleSystem::CreateParticleSystem(const QString &systemName)
{
    if (!ViewEnabled())
        return;
    if (renderer_.expired())
        return;
    RendererPtr renderer = renderer_.lock();

    try
    {
        EC_Placeable *placeable = dynamic_cast<EC_Placeable *>(FindPlaceable().get());
        if (!placeable)
        {
            LogError("Fail to create a new particle system, make sure that entity has EC_Placeable component created.");
            return;
        }

        Ogre::SceneManager* scene_mgr = renderer->GetSceneManager();
        Ogre::ParticleSystem* system = scene_mgr->createParticleSystem(renderer->GetUniqueObjectName("EC_Particlesystem"), SanitateAssetIdForOgre(systemName.toStdString()));
        if (system)
        {
            placeable->GetSceneNode()->attachObject(system);
            particleSystems_.push_back(system);
            system->setCastShadows(castShadows.Get());
            system->setRenderingDistance(renderingDistance.Get());
            return;
        }
    }
    catch (Ogre::Exception& e)
    {
        LogError("Could not add particle system " + Name().toStdString() + ": " + std::string(e.what()));
    }

    return;
}

void EC_ParticleSystem::DeleteParticleSystems()
{
    if (renderer_.expired())
        return;
    RendererPtr renderer = renderer_.lock();

    Ogre::SceneManager* scene_mgr = renderer->GetSceneManager();
    if (!scene_mgr)
        return;

    try
    {
        EC_Placeable *placeable = dynamic_cast<EC_Placeable *>(FindPlaceable().get());
        if (placeable)
        {
            Ogre::SceneNode *node = placeable->GetSceneNode();
            if (!node)
                return;
            for (unsigned i = 0; i < particleSystems_.size(); ++i)
                node->detachObject(particleSystems_[i]);
        }
        for (unsigned i = 0; i < particleSystems_.size(); ++i)
            scene_mgr->destroyParticleSystem(particleSystems_[i]);
    }
    catch (Ogre::Exception& /*e*/)
    {
        LogError("Could not delete particle systems");
    }

    particleSystems_.clear();

    return;
}

void EC_ParticleSystem::AttributeUpdated(IAttribute *attribute)
{
    if(attribute == &castShadows)
    {
        for (unsigned i = 0; i < particleSystems_.size(); ++i)
            particleSystems_[i]->setCastShadows(castShadows.Get());
    }
    else if (attribute == &renderingDistance)
    {
        for (unsigned i = 0; i < particleSystems_.size(); ++i)
            particleSystems_[i]->setRenderingDistance(renderingDistance.Get());
    }
    else if (attribute == &particleRef)
    {
        if (!ViewEnabled())
            return;

        // Request the new particle system resource. Once it has loaded, ParticleSystemAssetLoaded() will be called.
        AssetTransferPtr transfer = GetFramework()->Asset()->RequestAsset(particleRef.Get());
        if ( transfer.get() != 0)
        {
            connect(transfer.get(), SIGNAL(Loaded(IAssetTransfer*)), SLOT(ParticleSystemAssetLoaded()), Qt::UniqueConnection);
        }
        else
        {
            DeleteParticleSystems();
        }
    }
}

ComponentPtr EC_ParticleSystem::FindPlaceable() const
{
    if (GetParentEntity())
        return GetParentEntity()->GetComponent<EC_Placeable>();
    else
        return ComponentPtr();
}

void EC_ParticleSystem::ParticleSystemAssetLoaded()
{
    IAssetTransfer *transfer = dynamic_cast<IAssetTransfer*>(sender());
    assert(transfer);
    if (!transfer)
        return;

    OgreParticleAsset *asset = dynamic_cast<OgreParticleAsset*>(transfer->asset.get());
    if (!asset)
    {
        LogWarning("EC_ParticleSystem::ParticleSystemAssetLoaded: Asset transfer finished, but asset pointer was null!");
        return;
    }

    if (asset->GetNumTemplates() > 0)
    {
        DeleteParticleSystems();
        for (int i = 0 ; i< asset->GetNumTemplates(); ++i)
            CreateParticleSystem(asset->GetTemplateName(i));
    }
}

void EC_ParticleSystem::EntitySet()
{
    Scene::Entity *entity = this->GetParentEntity();
    if (!entity)
    {
        LogError("Failed to connect entity signals, component's parent entity is null");
        return;
    }

    QObject::connect(entity, SIGNAL(ComponentRemoved(IComponent*, AttributeChange::Type)), this, SLOT(OnComponentRemoved(IComponent*, AttributeChange::Type)), Qt::UniqueConnection);
}

void EC_ParticleSystem::OnComponentRemoved(IComponent *component, AttributeChange::Type change)
{
    // If the component is the Placeable, delete particle systems now for safety
    if (component->TypeName() == EC_Placeable::TypeNameStatic())
        DeleteParticleSystems();
}
<|endoftext|>


<commit_before>/*
 *  Copyright 2019-2021 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *
 *  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.
 *
 *  In no event and under no legal theory, whether in tort (including negligence),
 *  contract, or otherwise, unless required by applicable law (such as deliberate
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental,
 *  or consequential damages of any character arising as a result of this License or
 *  out of the use or inability to use the software (including but not limited to damages
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and
 *  all other commercial damages or losses), even if such Contributor has been advised
 *  of the possibility of such damages.
 */

#pragma once

/// \file
/// Implementation of the Diligent::StateObjectsRegistry template class

#include "DeviceObject.h"
#include <unordered_map>
#include "STDAllocator.hpp"

namespace Diligent
{
/// Template class implementing state object registry

/// \tparam ResourceDescType - type of the resource description. The type must have
///                            operator== and a hash function defined.
///
/// \remarks
/// The following strategies do not work:
/// - To store raw pointers and remove the object from the registry in the
///   object's destructor. In this case other thread may obtain a reference
///   to the object while it is being deleted. This scenario is possible if one thread
///   has just entered the destructor, but other is executing Find() and entered
///   the protection section.
/// - Strong pointers will cause circular references and result in memory leaks.
/// \remarks
/// Only weak pointers provide thread-safe solution. The object is either atomically destroyed,
/// so that no other thread can obtain a reference to it through weak pointers. Or it is atomically
/// locked, so that strong reference is obtained. In this case no other thread can destroy the object,
/// because there is at least one strong reference now. Note however that removing the object from the
/// registry in the object's destructor may cause a deadlock at the point where Find() locks the weak pointer:
/// if other thread has started dtor, the object will be locked by Diligent::RefCountedObject::Release().
/// If after that this thread locks the registry first, it will be waiting for the object to unlock in
/// Diligent::RefCntWeakPtr::Lock(), while the dtor thread will be waiting for the registry to unlock.
template <typename ResourceDescType>
class StateObjectsRegistry
{
public:
    /// Number of outstanding deleted objects to purge the registry.
    static constexpr int DeletedObjectsToPurge = 32;

    StateObjectsRegistry(IMemoryAllocator& RawAllocator, const Char* RegistryName) :
        m_DescToObjHashMap(STD_ALLOCATOR_RAW_MEM(HashMapElem, RawAllocator, "Allocator for unordered_map<ResourceDescType, RefCntWeakPtr<IDeviceObject> >")),
        m_RegistryName{RegistryName}
    {}

    ~StateObjectsRegistry()
    {
        // Object registry is part of the device, and every device
        // object holds a strong reference to the device. So device
        // is destroyed after all device objects are destroyed, and there
        // may only be expired references in the registry. After we
        // purge it, the registry must be empty.
        Purge();
        VERIFY(m_DescToObjHashMap.empty(), "DescToObjHashMap is not empty");
    }

    /// Adds a new object to the registry

    /// \param [in] ObjectDesc - object description.
    /// \param [in] pObject - pointer to the object.
    ///
    /// Besides adding a new object, the function also checks the number of
    /// outstanding deleted objects and calls Purge() if the number has reached
    /// the threshold value DeletedObjectsToPurge. Creating a state object is
    /// assumed to be an expensive operation and should be performed during
    /// the initialization. Occasional purge operations should not add significant
    /// cost to it.
    void Add(const ResourceDescType& ObjectDesc, IDeviceObject* pObject)
    {
        ThreadingTools::LockHelper Lock(m_LockFlag);

        // If the number of outstanding deleted objects reached the threshold value,
        // purge the registry. Since we have exclusive access now, it is safe
        // to do.
        if (m_NumDeletedObjects >= DeletedObjectsToPurge)
        {
            Purge();
            m_NumDeletedObjects = 0;
        }

        // Try to construct the new element in place
        auto Elems = m_DescToObjHashMap.emplace(std::make_pair(ObjectDesc, Diligent::RefCntWeakPtr<IDeviceObject>(pObject)));
        // It is theorertically possible that the same object can be found
        // in the registry. This might happen if two threads try to create
        // the same object at the same time. They both will not find the
        // object and then will create and try to add it.
        //
        // If the object already exists, we replace the existing reference.
        // This is safer as there might be scenarios where existing reference
        // might be expired. For instance, two threads try to create the same
        // object which is not in the registry. The first thread creates
        // the object, adds it to the registry and then releases it. After that
        // the second thread creates the same object and tries to add it to
        // the registry. It will find an existing expired reference to the
        // object.
        if (!Elems.second)
        {
            VERIFY(Elems.first->first == ObjectDesc, "Incorrect object description");
            LOG_WARNING_MESSAGE("Object named '", Elems.first->first.Name,
                                "' with the same description already exists in the registry."
                                "Replacing with the new object named '",
                                ObjectDesc.Name ? ObjectDesc.Name : "", "'.");
            Elems.first->second = pObject;
        }
    }

    /// Finds the object in the registry
    void Find(const ResourceDescType& Desc, IDeviceObject** ppObject)
    {
        VERIFY(*ppObject == nullptr, "Overwriting reference to existing object may cause memory leaks");
        *ppObject = nullptr;
        ThreadingTools::LockHelper Lock(m_LockFlag);

        auto It = m_DescToObjHashMap.find(Desc);
        if (It != m_DescToObjHashMap.end())
        {
            // Try to obtain strong reference to the object.
            // This is an atomic operation and we either get
            // a new strong reference or object has been destroyed
            // and we get null.
            auto pObject = It->second.Lock();
            if (pObject)
            {
                *ppObject = pObject.Detach();
                //LOG_INFO_MESSAGE( "Equivalent of the requested state object named \"", Desc.Name ? Desc.Name : "", "\" found in the ", m_RegistryName, " registry. Reusing existing object.");
            }
            else
            {
                // Expired object found: remove it from the map
                m_DescToObjHashMap.erase(It);
                Atomics::AtomicDecrement(m_NumDeletedObjects);
            }
        }
    }

    /// Purges outstanding deleted objects from the registry
    void Purge()
    {
        Uint32 NumPurgedObjects = 0;
        auto   It               = m_DescToObjHashMap.begin();
        while (It != m_DescToObjHashMap.end())
        {
            auto NextIt = It;
            ++NextIt;
            // Note that IsValid() is not a thread-safe function in the sense that it
            // can give false positive results. The only thread-safe way to check if the
            // object is alive is to lock the weak pointer, but that requires thread
            // synchronization. We will immediately unlock the pointer anyway, so we
            // want to detect 100% expired pointers. IsValid() does provide that information
            // because once a weak pointer becomes invalid, it will be invalid
            // until it is destroyed. It is not a problem if we miss an expired weak
            // pointer as it will definitiely be removed next time.
            if (!It->second.IsValid())
            {
                m_DescToObjHashMap.erase(It);
                ++NumPurgedObjects;
            }

            It = NextIt;
        }
        LOG_INFO_MESSAGE("Purged ", NumPurgedObjects, " deleted objects from the ", m_RegistryName, " registry");
    }

    /// Increments the number of outstanding deleted objects.
    /// When this number reaches DeletedObjectsToPurge, Purge() will
    /// be called.
    void ReportDeletedObject()
    {
        Atomics::AtomicIncrement(m_NumDeletedObjects);
    }

private:
    /// Lock flag to protect the m_DescToObjHashMap
    ThreadingTools::LockFlag m_LockFlag;

    /// Nmber of outstanding deleted objects that have not been purged
    Atomics::AtomicLong m_NumDeletedObjects;

    /// Hash map that stores weak pointers to the referenced objects
    typedef std::pair<const ResourceDescType, RefCntWeakPtr<IDeviceObject>>                                                                                HashMapElem;
    std::unordered_map<ResourceDescType, RefCntWeakPtr<IDeviceObject>, std::hash<ResourceDescType>, std::equal_to<ResourceDescType>, STDAllocatorRawMem<HashMapElem>> m_DescToObjHashMap;

    /// Registry name used for debug output
    const String m_RegistryName;
};
} // namespace Diligent
<commit_msg>Fixed uninitialized atomic in StateObjectsRegistry (#193)<commit_after>/*
 *  Copyright 2019-2021 Diligent Graphics LLC
 *  Copyright 2015-2019 Egor Yusov
 *
 *  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.
 *
 *  In no event and under no legal theory, whether in tort (including negligence),
 *  contract, or otherwise, unless required by applicable law (such as deliberate
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental,
 *  or consequential damages of any character arising as a result of this License or
 *  out of the use or inability to use the software (including but not limited to damages
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and
 *  all other commercial damages or losses), even if such Contributor has been advised
 *  of the possibility of such damages.
 */

#pragma once

/// \file
/// Implementation of the Diligent::StateObjectsRegistry template class

#include "DeviceObject.h"
#include <unordered_map>
#include "STDAllocator.hpp"

namespace Diligent
{
/// Template class implementing state object registry

/// \tparam ResourceDescType - type of the resource description. The type must have
///                            operator== and a hash function defined.
///
/// \remarks
/// The following strategies do not work:
/// - To store raw pointers and remove the object from the registry in the
///   object's destructor. In this case other thread may obtain a reference
///   to the object while it is being deleted. This scenario is possible if one thread
///   has just entered the destructor, but other is executing Find() and entered
///   the protection section.
/// - Strong pointers will cause circular references and result in memory leaks.
/// \remarks
/// Only weak pointers provide thread-safe solution. The object is either atomically destroyed,
/// so that no other thread can obtain a reference to it through weak pointers. Or it is atomically
/// locked, so that strong reference is obtained. In this case no other thread can destroy the object,
/// because there is at least one strong reference now. Note however that removing the object from the
/// registry in the object's destructor may cause a deadlock at the point where Find() locks the weak pointer:
/// if other thread has started dtor, the object will be locked by Diligent::RefCountedObject::Release().
/// If after that this thread locks the registry first, it will be waiting for the object to unlock in
/// Diligent::RefCntWeakPtr::Lock(), while the dtor thread will be waiting for the registry to unlock.
template <typename ResourceDescType>
class StateObjectsRegistry
{
public:
    /// Number of outstanding deleted objects to purge the registry.
    static constexpr int DeletedObjectsToPurge = 32;

    StateObjectsRegistry(IMemoryAllocator& RawAllocator, const Char* RegistryName) :
        m_NumDeletedObjects{0},
        m_DescToObjHashMap(STD_ALLOCATOR_RAW_MEM(HashMapElem, RawAllocator, "Allocator for unordered_map<ResourceDescType, RefCntWeakPtr<IDeviceObject> >")),
        m_RegistryName{RegistryName}
    {}

    ~StateObjectsRegistry()
    {
        // Object registry is part of the device, and every device
        // object holds a strong reference to the device. So device
        // is destroyed after all device objects are destroyed, and there
        // may only be expired references in the registry. After we
        // purge it, the registry must be empty.
        Purge();
        VERIFY(m_DescToObjHashMap.empty(), "DescToObjHashMap is not empty");
    }

    /// Adds a new object to the registry

    /// \param [in] ObjectDesc - object description.
    /// \param [in] pObject - pointer to the object.
    ///
    /// Besides adding a new object, the function also checks the number of
    /// outstanding deleted objects and calls Purge() if the number has reached
    /// the threshold value DeletedObjectsToPurge. Creating a state object is
    /// assumed to be an expensive operation and should be performed during
    /// the initialization. Occasional purge operations should not add significant
    /// cost to it.
    void Add(const ResourceDescType& ObjectDesc, IDeviceObject* pObject)
    {
        ThreadingTools::LockHelper Lock(m_LockFlag);

        // If the number of outstanding deleted objects reached the threshold value,
        // purge the registry. Since we have exclusive access now, it is safe
        // to do.
        if (m_NumDeletedObjects >= DeletedObjectsToPurge)
        {
            Purge();
            m_NumDeletedObjects = 0;
        }

        // Try to construct the new element in place
        auto Elems = m_DescToObjHashMap.emplace(std::make_pair(ObjectDesc, Diligent::RefCntWeakPtr<IDeviceObject>(pObject)));
        // It is theorertically possible that the same object can be found
        // in the registry. This might happen if two threads try to create
        // the same object at the same time. They both will not find the
        // object and then will create and try to add it.
        //
        // If the object already exists, we replace the existing reference.
        // This is safer as there might be scenarios where existing reference
        // might be expired. For instance, two threads try to create the same
        // object which is not in the registry. The first thread creates
        // the object, adds it to the registry and then releases it. After that
        // the second thread creates the same object and tries to add it to
        // the registry. It will find an existing expired reference to the
        // object.
        if (!Elems.second)
        {
            VERIFY(Elems.first->first == ObjectDesc, "Incorrect object description");
            LOG_WARNING_MESSAGE("Object named '", Elems.first->first.Name,
                                "' with the same description already exists in the registry."
                                "Replacing with the new object named '",
                                ObjectDesc.Name ? ObjectDesc.Name : "", "'.");
            Elems.first->second = pObject;
        }
    }

    /// Finds the object in the registry
    void Find(const ResourceDescType& Desc, IDeviceObject** ppObject)
    {
        VERIFY(*ppObject == nullptr, "Overwriting reference to existing object may cause memory leaks");
        *ppObject = nullptr;
        ThreadingTools::LockHelper Lock(m_LockFlag);

        auto It = m_DescToObjHashMap.find(Desc);
        if (It != m_DescToObjHashMap.end())
        {
            // Try to obtain strong reference to the object.
            // This is an atomic operation and we either get
            // a new strong reference or object has been destroyed
            // and we get null.
            auto pObject = It->second.Lock();
            if (pObject)
            {
                *ppObject = pObject.Detach();
                //LOG_INFO_MESSAGE( "Equivalent of the requested state object named \"", Desc.Name ? Desc.Name : "", "\" found in the ", m_RegistryName, " registry. Reusing existing object.");
            }
            else
            {
                // Expired object found: remove it from the map
                m_DescToObjHashMap.erase(It);
                Atomics::AtomicDecrement(m_NumDeletedObjects);
            }
        }
    }

    /// Purges outstanding deleted objects from the registry
    void Purge()
    {
        Uint32 NumPurgedObjects = 0;
        auto   It               = m_DescToObjHashMap.begin();
        while (It != m_DescToObjHashMap.end())
        {
            auto NextIt = It;
            ++NextIt;
            // Note that IsValid() is not a thread-safe function in the sense that it
            // can give false positive results. The only thread-safe way to check if the
            // object is alive is to lock the weak pointer, but that requires thread
            // synchronization. We will immediately unlock the pointer anyway, so we
            // want to detect 100% expired pointers. IsValid() does provide that information
            // because once a weak pointer becomes invalid, it will be invalid
            // until it is destroyed. It is not a problem if we miss an expired weak
            // pointer as it will definitiely be removed next time.
            if (!It->second.IsValid())
            {
                m_DescToObjHashMap.erase(It);
                ++NumPurgedObjects;
            }

            It = NextIt;
        }
        LOG_INFO_MESSAGE("Purged ", NumPurgedObjects, " deleted objects from the ", m_RegistryName, " registry");
    }

    /// Increments the number of outstanding deleted objects.
    /// When this number reaches DeletedObjectsToPurge, Purge() will
    /// be called.
    void ReportDeletedObject()
    {
        Atomics::AtomicIncrement(m_NumDeletedObjects);
    }

private:
    /// Lock flag to protect the m_DescToObjHashMap
    ThreadingTools::LockFlag m_LockFlag;

    /// Nmber of outstanding deleted objects that have not been purged
    Atomics::AtomicLong m_NumDeletedObjects;

    /// Hash map that stores weak pointers to the referenced objects
    typedef std::pair<const ResourceDescType, RefCntWeakPtr<IDeviceObject>>                                                                                HashMapElem;
    std::unordered_map<ResourceDescType, RefCntWeakPtr<IDeviceObject>, std::hash<ResourceDescType>, std::equal_to<ResourceDescType>, STDAllocatorRawMem<HashMapElem>> m_DescToObjHashMap;

    /// Registry name used for debug output
    const String m_RegistryName;
};
} // namespace Diligent
<|endoftext|>


<commit_before>/*
* This file is part of ATLAS. It is subject to the license terms in
* the LICENSE file found in the top-level directory of this distribution.
* (Also avialable at http://www.apache.org/licenses/LICENSE-2.0.txt)
* You may not use this file except in compliance with the License.
*/
#pragma once

#include "Poco/URI.h"
#include <Poco/DOM/AutoPtr.h>
#include <Poco/DOM/Document.h>
#include <map>
#include <assimp/scene.h>
#include <atlas/model/Folder.hpp>

namespace AssimpWorker {

	class ColladaMassager
	{
	public:
		ColladaMassager(const Poco::URI& colladaPath);

		~ColladaMassager();

		void readXML();
		void massage();
		void restoreOriginalNames(aiNode* node);
		void restoreOriginalNames(ATLAS::Model::Folder& folder);

		std::vector<std::pair<std::string, std::string>>& getExternalReferences();
		const float getCurrentUnit() const;
		const std::string getUpAxis();

	private:
		std::map<std::string, std::string> idToNameMap;
		Poco::URI uri;
		Poco::XML::AutoPtr<Poco::XML::Document> xmlDocument;
		bool needToPurge;
		std::vector<std::pair<std::string, std::string>> parentIDToExternalURL;
		int idCounter;
		bool alreadyMassagedMyFile;
		float localScaleBeforeMassage;
		std::string upAxis;

		void readUpAxis();
		void extractExternalReferences();
		bool isInternalReference(const std::string& url);
		const std::string& getURLOfReference(Poco::XML::Node* node);
		const std::string getIDOfParentNode(Poco::XML::Node* node);
		void purgeAllNodes();
		void purgeNode(Poco::XML::Node* node);
		void writePurgedXML();
		std::string getNameForId(const std::string& id);
		void forceUnitMeter();
	};

} // End namespace AssimpWorker

<commit_msg>fixup: missed a file in last commit (ec2c95)<commit_after>/*
* This file is part of ATLAS. It is subject to the license terms in
* the LICENSE file found in the top-level directory of this distribution.
* (Also avialable at http://www.apache.org/licenses/LICENSE-2.0.txt)
* You may not use this file except in compliance with the License.
*/
#pragma once

#include "Poco/URI.h"
#include <Poco/DOM/AutoPtr.h>
#include <Poco/DOM/Document.h>
#include <map>
#include <assimp/scene.h>
#include <atlas/model/Folder.hpp>

namespace AssimpWorker {

	class ColladaMassager
	{
	public:
		ColladaMassager(const Poco::URI& colladaPath);

		~ColladaMassager();

		void readXML();
		void massage();
		void restoreOriginalNames(aiNode* node);
		void restoreOriginalNames(ATLAS::Model::Folder& folder);

		std::vector<std::pair<std::string, std::string>>& getExternalReferences();
		const float getCurrentUnit() const;
		const std::string getUpAxis();

	private:
		std::map<std::string, std::string> idToNameMap;
		Poco::URI uri;
		Poco::XML::AutoPtr<Poco::XML::Document> xmlDocument;
		bool needToPurge;
		std::vector<std::pair<std::string, std::string>> parentIDToExternalURL;
		int idCounter;
		bool alreadyMassagedMyFile;
		float localScaleBeforeMassage;
		std::string upAxis;

		void readUpAxis();
		void extractExternalReferences();
		bool isInternalReference(const std::string& url);
		const std::string& getURLOfReference(Poco::XML::Node* node);
		const std::string getIDOfParentNode(Poco::XML::Node* node);
		void purgeAllNodes();
		void purgeNode(Poco::XML::Node* node);
		void writePurgedXML();
		std::string getNameForId(const std::string& id);
		void readUnit();
	};

} // End namespace AssimpWorker

<|endoftext|>


<commit_before>/**
 * Copyright (c) Sjors Gielen, 2011-2012
 * See LICENSE for license.
 */

#include <QtCore/QList>
#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>

#include "karmaplugin.h"

int main(int argc, char *argv[]) {
	if(argc < 2) {
		qWarning() << "Usage: dazeus-plugin-karma socketfile";
		return 1;
	}
	QString socketfile = argv[1];
	QCoreApplication app(argc, argv);
	KarmaPlugin kp(socketfile);
	return app.exec();
}

KarmaPlugin::KarmaPlugin(const QString &socketfile)
: QObject()
, d(new DaZeus())
{
	if(!d->open(socketfile) || !d->subscribe("PRIVMSG")) {
		qWarning() << d->error();
		delete d;
		d = 0;
		return;
	}
	connect(d,    SIGNAL(newEvent(DaZeus::Event*)),
	        this, SLOT(  newEvent(DaZeus::Event*)));
	connect(d,    SIGNAL(connectionFailed()),
	        this, SLOT(  connectionFailed()));
}

KarmaPlugin::~KarmaPlugin() {
	delete d;
}

void KarmaPlugin::connectionFailed() {
	// TODO: handle this better
	qWarning() << "Error: connection failed: " << d->error();
	delete d;
	d = 0;
	QCoreApplication::exit(1);
}

void KarmaPlugin::modifyKarma(const QString &network, const QString &object, bool increase, int & newUp, int & newDown) {
	DaZeus::Scope s(network);
	DaZeus::Scope g;

	QString qualifiedName = QLatin1String("perl.DazKarma.karma_") + object.toLower();
	QString karmaUpName   = QLatin1String("perl.DazKarma.upkarma_") + object.toLower();
	QString karmaDownName = QLatin1String("perl.DazKarma.downkarma_") + object.toLower();

	int current = d->getProperty(qualifiedName, s).toInt();
	newUp = d->getProperty(karmaUpName, s).toInt();
	newDown = d->getProperty(karmaDownName, s).toInt();

	if((current == 0 || newUp == 0 || newDown == 0) && !d->error().isNull()) {
		qWarning() << "Could not getProperty(): " << d->error();
	}

	// Check for non-consistent karma and adjust counters accordingly
	if(current < (newUp - newDown)) newDown += -current - (newUp - newDown);
	if(current > (newUp - newDown)) newUp += current - (newUp - newDown);

	if(increase) {
		++current;
		++newUp;
	} else {
		--current;
		++newDown;
	}

	bool res, upres, downres;
	if(current == 0) {
		res = d->unsetProperty(qualifiedName, s);
		// Also unset global property, in case one is left behind
		if(res) res = d->unsetProperty(qualifiedName, g);

		// Set counters to match with neutral karma
		upres = d->setProperty(karmaUpName, QString::number(newUp), s);
		downres = d->setProperty(karmaDownName, QString::number(newDown), s);
	} else {
		res = d->setProperty(qualifiedName, QString::number(current), s);
		upres = d->setProperty(karmaUpName, QString::number(newUp), s);
		downres = d->setProperty(karmaDownName, QString::number(newDown), s);
	}
	if(!res || !upres || !downres) {
		qWarning() << "Could not (un)setProperty(): " << d->error();
	}
}

void KarmaPlugin::getKarma(const QString &network, const QString &object, int & currUp, int & currDown) {
	DaZeus::Scope s(network);

	int current = d->getProperty("perl.DazKarma.karma_" + object.toLower(), s).toInt();
	currUp = d->getProperty("perl.DazKarma.upkarma_" + object.toLower(), s).toInt();
	currDown = d->getProperty("perl.DazKarma.downkarma_" + object.toLower(), s).toInt();

	// Check for non-consistent karma and adjust counters accordingly
	if (current < (currUp - currDown)) currDown += -current - (currUp - currDown);
	if (current > (currUp - currDown)) currUp += current - (currUp - currDown);
}

void KarmaPlugin::newEvent(DaZeus::Event *e) {
	if(e->event != "PRIVMSG") return;
	if(e->parameters.size() < 4) {
		qWarning() << "Incorrect parameter size for message received";
		return;
	}
	QString network = e->parameters[0];
	QString origin  = e->parameters[1];
	QString recv    = e->parameters[2];
	QString message = e->parameters[3];
	// TODO: use getNick()
	if(!recv.startsWith('#')) {
		// reply to PM
		recv = origin;
	}
	if(message.startsWith("}karma ")) {
		QString object = message.mid(7).trimmed();
		int currUp, currDown;
		getKarma(network, object, currUp, currDown);

		bool res;
		if((currUp - currDown) == 0) {
			if(!d->error().isNull()) {
				qWarning() << "Failed to fetch karma: " << d->error();
			}
			res = d->message(network, recv, object + " has neutral karma (+" + QString::number(currUp) + ", -" + QString::number(currDown) + ").");
		} else {
			res = d->message(network, recv, object + " has a karma of " + QString::number(currUp - currDown) + " (+" + QString::number(currUp) + ", -" + QString::number(currDown) + ").");
		}
		if(!res) {
			qWarning() << "Failed to send message: " << d->error();
		}
		return;
	}

	// Walk through the message searching for -- and ++; upon finding
	// either, reconstruct what the object was.
	// Start searching from i=1 because a string starting with -- or
	// ++ means nothing anyway, and now we can always ask for b[i-1]
	// End search at one character from the end so we can always ask
	// for b[i+1]
	QList<int> hits;
	int len = message.length();
	for(int i = 1; i < (len - 1); ++i) {
		bool wordEnd = i == len - 2 // End of string
		    || message[i+2].isSpace() // A space character
		    || message[i+2] == QLatin1Char(',') // Comma
		    || message[i+2] == QLatin1Char('.') // Dot
		    || message[i+2] == QLatin1Char(';') // Semicolon
		    || message[i+2] == QLatin1Char(':'); // Colon
		if( message[i] == QLatin1Char('-') && message[i+1] == QLatin1Char('-') && wordEnd ) {
			hits.append(i);
		}
		else if( message[i] == QLatin1Char('+') && message[i+1] == QLatin1Char('+') && wordEnd ) {
			hits.append(i);
		}
	}

	QListIterator<int> i(hits);
	while(i.hasNext()) {
		int pos = i.next();
		bool isIncrease = message[pos] == QLatin1Char('+');
		QString object;
		int newUp, newDown;

		if(message[pos-1].isLetter()) {
			// only alphanumerics between startPos and pos-1
			int startPos = pos - 1;
			for(; startPos >= 0; --startPos) {
				if(!message[startPos].isLetter()
				&& !message[startPos].isDigit()
				&& message[startPos] != QLatin1Char('-')
				&& message[startPos] != QLatin1Char('_'))
				{
					// revert the negation
					startPos++;
					break;
				}
			}
			if(startPos > 0 && !message[startPos-1].isSpace()) {
				// non-alphanumerics would be in this object, ignore it
				continue;
			}
			object = message.mid(startPos, pos - startPos);
			modifyKarma(network, object, isIncrease, newUp, newDown);
			qDebug() << origin << (isIncrease ? "increased" : "decreased")
			         << "karma of" << object << " to " << QString::number(newUp - newDown)
					 << " (+" << QString::number(newUp) << ", -" << QString::number(newDown) << ").";
			continue;
		}

		char beginner;
		char ender;
		if(message[pos-1] == QLatin1Char(']')) {
			beginner = '[';
			ender = ']';
		} else if(message[pos-1] == QLatin1Char(')')) {
			beginner = '(';
			ender = ')';
		} else {
			continue;
		}

		// find the last $beginner before $ender
		int startPos = message.lastIndexOf(QLatin1Char(beginner), pos);
		// unless there's already an $ender between them
		if(message.indexOf(QLatin1Char(ender), startPos) < pos - 1)
			continue;

		object = message.mid(startPos + 1, pos - 2 - startPos);
		modifyKarma(network, object, isIncrease, newUp, newDown);
		QString message = origin + (isIncrease ? " increased" : " decreased") + " karma of "
			         + object + " to " + QString::number(newUp - newDown)
					 + " (+" + QString::number(newUp) + ", -" + QString::number(newDown) + ").";
		qDebug() << message;
		if(ender == ']') {
			// Verbose mode, print the result
			if(!d->message(network, recv, message)) {
				qWarning() << "Failed to send message: " << d->error();
			}
		}
	}
}
<commit_msg>Use ::getKarma() inside ::modifyKarma()<commit_after>/**
 * Copyright (c) Sjors Gielen, 2011-2012
 * See LICENSE for license.
 */

#include <QtCore/QList>
#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>

#include "karmaplugin.h"

int main(int argc, char *argv[]) {
	if(argc < 2) {
		qWarning() << "Usage: dazeus-plugin-karma socketfile";
		return 1;
	}
	QString socketfile = argv[1];
	QCoreApplication app(argc, argv);
	KarmaPlugin kp(socketfile);
	return app.exec();
}

KarmaPlugin::KarmaPlugin(const QString &socketfile)
: QObject()
, d(new DaZeus())
{
	if(!d->open(socketfile) || !d->subscribe("PRIVMSG")) {
		qWarning() << d->error();
		delete d;
		d = 0;
		return;
	}
	connect(d,    SIGNAL(newEvent(DaZeus::Event*)),
	        this, SLOT(  newEvent(DaZeus::Event*)));
	connect(d,    SIGNAL(connectionFailed()),
	        this, SLOT(  connectionFailed()));
}

KarmaPlugin::~KarmaPlugin() {
	delete d;
}

void KarmaPlugin::connectionFailed() {
	// TODO: handle this better
	qWarning() << "Error: connection failed: " << d->error();
	delete d;
	d = 0;
	QCoreApplication::exit(1);
}

void KarmaPlugin::modifyKarma(const QString &network, const QString &object, bool increase, int & newUp, int & newDown) {
	DaZeus::Scope s(network);
	DaZeus::Scope g;

	getKarma(network, object, newUp, newDown);
	int current = newUp - newDown;

	if(increase) {
		++current;
		++newUp;
	} else {
		--current;
		++newDown;
	}

	QString qualifiedName = "perl.DazKarma.karma_" + object.toLower();
	QString karmaUpName   = "perl.DazKarma.upkarma_" + object.toLower();
	QString karmaDownName = "perl.DazKarma.downkarma_" + object.toLower();

	bool success, successUp, successDown;
	if(current == 0) {
		success = d->unsetProperty(qualifiedName, s);
		if(success) {
			// Also unset global property, in case one is left behind
			success = d->unsetProperty(qualifiedName, g);
		}

		// Set counters to match with neutral karma
		successUp = d->setProperty(karmaUpName, QString::number(newUp), s);
		successDown = d->setProperty(karmaDownName, QString::number(newDown), s);
	} else {
		success = d->setProperty(qualifiedName, QString::number(current), s);
		successUp = d->setProperty(karmaUpName, QString::number(newUp), s);
		successDown = d->setProperty(karmaDownName, QString::number(newDown), s);
	}
	if(!(success && successUp && successDown)) {
		qWarning() << "Could not (un)setProperty(): " << d->error();
	}
}

void KarmaPlugin::getKarma(const QString &network, const QString &object, int & currUp, int & currDown) {
	DaZeus::Scope s(network);

	int current = d->getProperty("perl.DazKarma.karma_" + object.toLower(), s).toInt();
	currUp = d->getProperty("perl.DazKarma.upkarma_" + object.toLower(), s).toInt();
	currDown = d->getProperty("perl.DazKarma.downkarma_" + object.toLower(), s).toInt();

	if((current == 0 || currUp == 0 || currDown == 0) && !d->error().isNull()) {
		qWarning() << "Could not getProperty(): " << d->error();
	}

	// Check for non-consistent karma and adjust counters accordingly
	if (current < (currUp - currDown)) currDown += -current - (currUp - currDown);
	if (current > (currUp - currDown)) currUp += current - (currUp - currDown);
}

void KarmaPlugin::newEvent(DaZeus::Event *e) {
	if(e->event != "PRIVMSG") return;
	if(e->parameters.size() < 4) {
		qWarning() << "Incorrect parameter size for message received";
		return;
	}
	QString network = e->parameters[0];
	QString origin  = e->parameters[1];
	QString recv    = e->parameters[2];
	QString message = e->parameters[3];
	// TODO: use getNick()
	if(!recv.startsWith('#')) {
		// reply to PM
		recv = origin;
	}
	if(message.startsWith("}karma ")) {
		QString object = message.mid(7).trimmed();
		int currUp, currDown;
		getKarma(network, object, currUp, currDown);

		bool res;
		if((currUp - currDown) == 0) {
			if(!d->error().isNull()) {
				qWarning() << "Failed to fetch karma: " << d->error();
			}
			res = d->message(network, recv, object + " has neutral karma (+" + QString::number(currUp) + ", -" + QString::number(currDown) + ").");
		} else {
			res = d->message(network, recv, object + " has a karma of " + QString::number(currUp - currDown) + " (+" + QString::number(currUp) + ", -" + QString::number(currDown) + ").");
		}
		if(!res) {
			qWarning() << "Failed to send message: " << d->error();
		}
		return;
	}

	// Walk through the message searching for -- and ++; upon finding
	// either, reconstruct what the object was.
	// Start searching from i=1 because a string starting with -- or
	// ++ means nothing anyway, and now we can always ask for b[i-1]
	// End search at one character from the end so we can always ask
	// for b[i+1]
	QList<int> hits;
	int len = message.length();
	for(int i = 1; i < (len - 1); ++i) {
		bool wordEnd = i == len - 2 // End of string
		    || message[i+2].isSpace() // A space character
		    || message[i+2] == QLatin1Char(',') // Comma
		    || message[i+2] == QLatin1Char('.') // Dot
		    || message[i+2] == QLatin1Char(';') // Semicolon
		    || message[i+2] == QLatin1Char(':'); // Colon
		if( message[i] == QLatin1Char('-') && message[i+1] == QLatin1Char('-') && wordEnd ) {
			hits.append(i);
		}
		else if( message[i] == QLatin1Char('+') && message[i+1] == QLatin1Char('+') && wordEnd ) {
			hits.append(i);
		}
	}

	QListIterator<int> i(hits);
	while(i.hasNext()) {
		int pos = i.next();
		bool isIncrease = message[pos] == QLatin1Char('+');
		QString object;
		int newUp, newDown;

		if(message[pos-1].isLetter()) {
			// only alphanumerics between startPos and pos-1
			int startPos = pos - 1;
			for(; startPos >= 0; --startPos) {
				if(!message[startPos].isLetter()
				&& !message[startPos].isDigit()
				&& message[startPos] != QLatin1Char('-')
				&& message[startPos] != QLatin1Char('_'))
				{
					// revert the negation
					startPos++;
					break;
				}
			}
			if(startPos > 0 && !message[startPos-1].isSpace()) {
				// non-alphanumerics would be in this object, ignore it
				continue;
			}
			object = message.mid(startPos, pos - startPos);
			modifyKarma(network, object, isIncrease, newUp, newDown);
			qDebug() << origin << (isIncrease ? "increased" : "decreased")
			         << "karma of" << object << " to " << QString::number(newUp - newDown)
					 << " (+" << QString::number(newUp) << ", -" << QString::number(newDown) << ").";
			continue;
		}

		char beginner;
		char ender;
		if(message[pos-1] == QLatin1Char(']')) {
			beginner = '[';
			ender = ']';
		} else if(message[pos-1] == QLatin1Char(')')) {
			beginner = '(';
			ender = ')';
		} else {
			continue;
		}

		// find the last $beginner before $ender
		int startPos = message.lastIndexOf(QLatin1Char(beginner), pos);
		// unless there's already an $ender between them
		if(message.indexOf(QLatin1Char(ender), startPos) < pos - 1)
			continue;

		object = message.mid(startPos + 1, pos - 2 - startPos);
		modifyKarma(network, object, isIncrease, newUp, newDown);
		QString message = origin + (isIncrease ? " increased" : " decreased") + " karma of "
			         + object + " to " + QString::number(newUp - newDown)
					 + " (+" + QString::number(newUp) + ", -" + QString::number(newDown) + ").";
		qDebug() << message;
		if(ender == ']') {
			// Verbose mode, print the result
			if(!d->message(network, recv, message)) {
				qWarning() << "Failed to send message: " << d->error();
			}
		}
	}
}
<|endoftext|>


<commit_before>#include "ltlcontext/ltlcontext.hpp"

#include <ucontext.h>
#include <assert.h>
#include <vector>

#include <new>

#ifdef __APPLE__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

namespace ltl {

struct context
{
    explicit context()
    {
        getcontext(&value);
    }
    ucontext_t value;
};

namespace {
struct secondary_context : context
{
    explicit secondary_context(std::size_t stack_size)
    : context()
    , stack(stack_size)
    {

    }
    std::vector<char> stack;
};
} // namespace

static_assert(sizeof(context) >= sizeof(ucontext_t), "sizeof context to small");

void jump(context* ofc, context* nfc)
{
    swapcontext(&ofc->value, &nfc->value);
}

context* create_context(std::size_t stack_size, void (*fn)(void*), void* vp)
{
    auto ret = new secondary_context(stack_size);
    ucontext_t* ctx = &ret->value;
    ctx->uc_stack.ss_size = stack_size;
    ctx->uc_stack.ss_sp = ret->stack.data();
    ctx->uc_link = 0;
    makecontext(ctx, reinterpret_cast<void(*)()>(fn), 1, vp);

    return ret;
}

void destroy_context(context* ctx)
{
    delete static_cast<secondary_context*>(ctx);
}

context* create_main_context()
{
    return new context();
}

void destroy_main_context(context* ctx)
{
    delete ctx;
}

} // namespace ltl

#ifdef __APPLE__
#pragma GCC diagnostic pop
#endif
<commit_msg>Minor changes, removed explicit, whitespace.<commit_after>#include "ltlcontext/ltlcontext.hpp"

#include <ucontext.h>
#include <assert.h>
#include <vector>

#include <new>

#ifdef __APPLE__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

namespace ltl {

struct context
{
    context()
    {
        getcontext(&value);
    }
    ucontext_t value;
};

namespace {
struct secondary_context : context
{
    explicit secondary_context(std::size_t stack_size)
    : context()
    , stack(stack_size)
    {
    }
    std::vector<char> stack;
};
} // namespace

static_assert(sizeof(context) >= sizeof(ucontext_t), "sizeof context to small");

void jump(context* ofc, context* nfc)
{
    swapcontext(&ofc->value, &nfc->value);
}

context* create_context(std::size_t stack_size, void (*fn)(void*), void* vp)
{
    auto ret = new secondary_context(stack_size);
    ucontext_t* ctx = &ret->value;
    ctx->uc_stack.ss_size = stack_size;
    ctx->uc_stack.ss_sp = ret->stack.data();
    ctx->uc_link = 0;
    makecontext(ctx, reinterpret_cast<void(*)()>(fn), 1, vp);

    return ret;
}

void destroy_context(context* ctx)
{
    delete static_cast<secondary_context*>(ctx);
}

context* create_main_context()
{
    return new context();
}

void destroy_main_context(context* ctx)
{
    delete ctx;
}

} // namespace ltl

#ifdef __APPLE__
#pragma GCC diagnostic pop
#endif
<|endoftext|>


<commit_before>#include <fstream>

#include "gtest.h"
#include "util.hpp"

#include <cell.hpp>
#include <fvm.hpp>

#include <json/src/json.hpp>

// compares results with those generated by nrn/soma.py
// single compartment model with HH channels
TEST(soma, neuron_baseline)
{
    using namespace nest::mc;
    using namespace nlohmann;

    nest::mc::cell cell;

    // setup global state for the mechanisms
    nest::mc::mechanisms::setup_mechanism_helpers();

    // Soma with diameter 18.8um and HH channel
    auto soma = cell.add_soma(18.8/2.0);
    soma->mechanism("membrane").set("r_L", 123); // no effect for single compartment cell
    soma->add_mechanism(hh_parameters());

    // add stimulus to the soma
    cell.add_stimulus({0,0.5}, {10., 100., 0.1});

    // make the lowered finite volume cell
    fvm::fvm_cell<double, int> model(cell);

    // load data from file
    auto cell_data = testing::load_spike_data("../nrn/soma.json");
    EXPECT_TRUE(cell_data.size()>0);
    if(cell_data.size()==0) return;

    json& nrn =
        *std::min_element(
            cell_data.begin(), cell_data.end(),
            [](json& lhs, json& rhs) {return lhs["dt"]<rhs["dt"];}
        );
    std::vector<double> nrn_spike_times = nrn["spikes"];

    std::cout << "baseline with time step size " << nrn["dt"] << " ms\n";
    std::cout << "baseline spikes : " << nrn["spikes"] << "\n";

    for(auto& run : cell_data) {
        std::vector<double> v;
        double dt = run["dt"];

        // set initial conditions
        using memory::all;
        model.voltage()(all) = -65.;
        model.initialize(); // have to do this _after_ initial conditions are set

        // run the simulation
        auto tfinal =   120.; // ms
        int nt = tfinal/dt;
        v.push_back(model.voltage()[0]);
        for(auto i=0; i<nt; ++i) {
            model.advance(dt);
            // save voltage at soma
            v.push_back(model.voltage()[0]);
        }

        // get the spike times from the NEST MC simulation
        auto nst_spike_times = testing::find_spikes(v, 0., dt);
        // compare NEST MC and baseline NEURON results
        auto comparison = testing::compare_spikes(nst_spike_times, nrn_spike_times);

        // Assert that relative error is less than 1%.
        // For a 100 ms simulation this asserts that the difference between
        // NEST and the most accurate NEURON simulation is less than 1 ms.
        std::cout << "MAX ERROR @ " << dt << " is " << comparison.max_relative_error()*100. << "\n";
        EXPECT_TRUE(comparison.max_relative_error()*100. < 1.);
    }
}

// check if solution converges
TEST(soma, convergence)
{
    using namespace nest::mc;

    nest::mc::cell cell;

    // setup global state for the mechanisms
    nest::mc::mechanisms::setup_mechanism_helpers();

    // Soma with diameter 18.8um and HH channel
    auto soma = cell.add_soma(18.8/2.0);
    soma->mechanism("membrane").set("r_L", 123); // no effect for single compartment cell
    soma->add_mechanism(hh_parameters());

    // add stimulus to the soma
    cell.add_stimulus({0,0.5}, {10., 100., 0.1});

    // make the lowered finite volume cell
    fvm::fvm_cell<double, int> model(cell);

    // generate baseline solution with small dt=0.0001
    std::vector<double> baseline_spike_times;
    {
        auto dt = 1e-4;
        std::vector<double> v;

        // set initial conditions
        using memory::all;
        model.voltage()(all) = -65.;
        model.initialize(); // have to do this _after_ initial conditions are set

        // run the simulation
        auto tfinal =   120.; // ms
        int nt = tfinal/dt;
        v.push_back(model.voltage()[0]);
        for(auto i=0; i<nt; ++i) {
            model.advance(dt);
            // save voltage at soma
            v.push_back(model.voltage()[0]);
        }

        baseline_spike_times = testing::find_spikes(v, 0., dt);
    }

    testing::spike_comparison previous;
    for(auto dt : {0.05, 0.02, 0.01, 0.005, 0.001}) {
        std::vector<double> v;

        // set initial conditions
        using memory::all;
        model.voltage()(all) = -65.;
        model.initialize(); // have to do this _after_ initial conditions are set

        // run the simulation
        auto tfinal =   120.; // ms
        int nt = tfinal/dt;
        v.push_back(model.voltage()[0]);
        for(auto i=0; i<nt; ++i) {
            model.advance(dt);
            // save voltage at soma
            v.push_back(model.voltage()[0]);
        }

        // get the spike times from the NEST MC simulation
        auto nst_spike_times = testing::find_spikes(v, 0., dt);

        // compare NEST MC and baseline NEURON results
        auto comparison = testing::compare_spikes(nst_spike_times, baseline_spike_times);
        std::cout << "dt " << std::setw(8) << dt << " : " << comparison << "\n";
        if(!previous.is_valid()) {
            previous = std::move(comparison);
        }
        else {
            // Assert that relative error is less than 1%.
            // For a 100 ms simulation this asserts that the difference between
            // NEST and the most accurate NEURON simulation is less than 1 ms.
            EXPECT_TRUE(comparison.max_relative_error() < previous.max_relative_error());
            EXPECT_TRUE(comparison.rms < previous.rms);
            EXPECT_TRUE(comparison.max < previous.max);
        }
    }
}
<commit_msg>Remove another indirect include<commit_after>#include <fstream>

#include "gtest.h"
#include "util.hpp"

#include <cell.hpp>
#include <fvm.hpp>

#include <json.hpp>

// compares results with those generated by nrn/soma.py
// single compartment model with HH channels
TEST(soma, neuron_baseline)
{
    using namespace nest::mc;
    using namespace nlohmann;

    nest::mc::cell cell;

    // setup global state for the mechanisms
    nest::mc::mechanisms::setup_mechanism_helpers();

    // Soma with diameter 18.8um and HH channel
    auto soma = cell.add_soma(18.8/2.0);
    soma->mechanism("membrane").set("r_L", 123); // no effect for single compartment cell
    soma->add_mechanism(hh_parameters());

    // add stimulus to the soma
    cell.add_stimulus({0,0.5}, {10., 100., 0.1});

    // make the lowered finite volume cell
    fvm::fvm_cell<double, int> model(cell);

    // load data from file
    auto cell_data = testing::load_spike_data("../nrn/soma.json");
    EXPECT_TRUE(cell_data.size()>0);
    if(cell_data.size()==0) return;

    json& nrn =
        *std::min_element(
            cell_data.begin(), cell_data.end(),
            [](json& lhs, json& rhs) {return lhs["dt"]<rhs["dt"];}
        );
    std::vector<double> nrn_spike_times = nrn["spikes"];

    std::cout << "baseline with time step size " << nrn["dt"] << " ms\n";
    std::cout << "baseline spikes : " << nrn["spikes"] << "\n";

    for(auto& run : cell_data) {
        std::vector<double> v;
        double dt = run["dt"];

        // set initial conditions
        using memory::all;
        model.voltage()(all) = -65.;
        model.initialize(); // have to do this _after_ initial conditions are set

        // run the simulation
        auto tfinal =   120.; // ms
        int nt = tfinal/dt;
        v.push_back(model.voltage()[0]);
        for(auto i=0; i<nt; ++i) {
            model.advance(dt);
            // save voltage at soma
            v.push_back(model.voltage()[0]);
        }

        // get the spike times from the NEST MC simulation
        auto nst_spike_times = testing::find_spikes(v, 0., dt);
        // compare NEST MC and baseline NEURON results
        auto comparison = testing::compare_spikes(nst_spike_times, nrn_spike_times);

        // Assert that relative error is less than 1%.
        // For a 100 ms simulation this asserts that the difference between
        // NEST and the most accurate NEURON simulation is less than 1 ms.
        std::cout << "MAX ERROR @ " << dt << " is " << comparison.max_relative_error()*100. << "\n";
        EXPECT_TRUE(comparison.max_relative_error()*100. < 1.);
    }
}

// check if solution converges
TEST(soma, convergence)
{
    using namespace nest::mc;

    nest::mc::cell cell;

    // setup global state for the mechanisms
    nest::mc::mechanisms::setup_mechanism_helpers();

    // Soma with diameter 18.8um and HH channel
    auto soma = cell.add_soma(18.8/2.0);
    soma->mechanism("membrane").set("r_L", 123); // no effect for single compartment cell
    soma->add_mechanism(hh_parameters());

    // add stimulus to the soma
    cell.add_stimulus({0,0.5}, {10., 100., 0.1});

    // make the lowered finite volume cell
    fvm::fvm_cell<double, int> model(cell);

    // generate baseline solution with small dt=0.0001
    std::vector<double> baseline_spike_times;
    {
        auto dt = 1e-4;
        std::vector<double> v;

        // set initial conditions
        using memory::all;
        model.voltage()(all) = -65.;
        model.initialize(); // have to do this _after_ initial conditions are set

        // run the simulation
        auto tfinal =   120.; // ms
        int nt = tfinal/dt;
        v.push_back(model.voltage()[0]);
        for(auto i=0; i<nt; ++i) {
            model.advance(dt);
            // save voltage at soma
            v.push_back(model.voltage()[0]);
        }

        baseline_spike_times = testing::find_spikes(v, 0., dt);
    }

    testing::spike_comparison previous;
    for(auto dt : {0.05, 0.02, 0.01, 0.005, 0.001}) {
        std::vector<double> v;

        // set initial conditions
        using memory::all;
        model.voltage()(all) = -65.;
        model.initialize(); // have to do this _after_ initial conditions are set

        // run the simulation
        auto tfinal =   120.; // ms
        int nt = tfinal/dt;
        v.push_back(model.voltage()[0]);
        for(auto i=0; i<nt; ++i) {
            model.advance(dt);
            // save voltage at soma
            v.push_back(model.voltage()[0]);
        }

        // get the spike times from the NEST MC simulation
        auto nst_spike_times = testing::find_spikes(v, 0., dt);

        // compare NEST MC and baseline NEURON results
        auto comparison = testing::compare_spikes(nst_spike_times, baseline_spike_times);
        std::cout << "dt " << std::setw(8) << dt << " : " << comparison << "\n";
        if(!previous.is_valid()) {
            previous = std::move(comparison);
        }
        else {
            // Assert that relative error is less than 1%.
            // For a 100 ms simulation this asserts that the difference between
            // NEST and the most accurate NEURON simulation is less than 1 ms.
            EXPECT_TRUE(comparison.max_relative_error() < previous.max_relative_error());
            EXPECT_TRUE(comparison.rms < previous.rms);
            EXPECT_TRUE(comparison.max < previous.max);
        }
    }
}
<|endoftext|>


<commit_before>/**
 * @file linear_regression.cpp
 * @author James Cline
 * @author Michael Fox
 *
 * Implementation of simple linear regression.
 */
#include "linear_regression.hpp"

using namespace mlpack;
using namespace mlpack::regression;

LinearRegression::LinearRegression(const arma::mat& predictors,
																	 const arma::vec& responses,
																	 const double lambda,
																	 const bool intercept,
																	 const arma::vec& weights
																	 ) :
    lambda(lambda),
    intercept(intercept)
{
  /*
   * We want to calculate the a_i coefficients of:
   * \sum_{i=0}^n (a_i * x_i^i)
   * In order to get the intercept value, we will add a row of ones.
   */

  // We store the number of rows and columns of the predictors.
  // Reminder: Armadillo stores the data transposed from how we think of it,
  //           that is, columns are actually rows (see: column major order).
  const size_t nCols = predictors.n_cols;

  arma::mat p = predictors;
	arma::vec r = responses;
	// Here we add the row of ones to the predictors.
	// The intercept is not penalized. Add an "all ones" row to design and set
	// intercept = false to get a penalized intercept
	if(intercept)
	{
		p.insert_rows(0, arma::ones<arma::mat>(1,nCols));
	}

 	if(weights.n_elem > 0)
	{
	  p = p * diagmat(sqrt(weights));
    r =  sqrt(weights) % responses;
	}

  if (lambda != 0.0)
  {
    // Add the identity matrix to the predictors (this is equivalent to ridge
    // regression).  See http://math.stackexchange.com/questions/299481/ for
    // more information.
		p.insert_cols(nCols, predictors.n_rows);
    p.submat(p.n_rows - predictors.n_rows, nCols, p.n_rows - 1, nCols +
		predictors.n_rows - 1) = sqrt(lambda) * arma::eye<arma::mat>(predictors.n_rows,
		    predictors.n_rows);
  }

  // We compute the QR decomposition of the predictors.
  // We transpose the predictors because they are in column major order.
  arma::mat Q, R;
  arma::qr(Q, R, arma::trans(p));

  // We compute the parameters, B, like so:
  // R * B = Q^T * responses
  // B = Q^T * responses * R^-1
  // If lambda > 0, then we must add a bunch of empty responses.
  if (lambda == 0.0)
  {
    arma::solve(parameters, R, arma::trans(Q) * r);
  }
  else
  {
    // Copy responses into larger vector.
		r.insert_rows(nCols,p.n_cols - nCols);
    arma::solve(parameters, R, arma::trans(Q) * r);
  }
}

LinearRegression::LinearRegression(const std::string& filename) :
    lambda(0.0)
{
  arma::mat parameter;
  data::Load(filename, parameter, true);
  parameters = parameter.unsafe_col(0);
}

LinearRegression::LinearRegression(const LinearRegression& linearRegression) :
    parameters(linearRegression.parameters),
    lambda(linearRegression.lambda)
{ /* Nothing to do. */ }

void LinearRegression::Predict(const arma::mat& points, arma::vec& predictions)
    const
{
	if (intercept)
	{
		// We want to be sure we have the correct number of dimensions in the dataset.
    Log::Assert(points.n_rows == parameters.n_rows-1);
		// Get the predictions, but this ignores the intercept value (parameters[0]).
	  predictions = arma::trans(arma::trans(parameters.subvec(1, parameters.n_elem - 1)) * points);
		// Now add the intercept.
		predictions += parameters(0);
	}
	else
	{
		// We want to be sure we have the correct number of dimensions in the dataset.
    Log::Assert(points.n_rows == parameters.n_rows);
		predictions = arma::trans(arma::trans(parameters) * points);
	}

}

//! Compute the L2 squared error on the given predictors and responses.
double LinearRegression::ComputeError(const arma::mat& predictors,
                                      const arma::vec& responses) const
{
  // Get the number of columns and rows of the dataset.
  const size_t nCols = predictors.n_cols;
  const size_t nRows = predictors.n_rows;

  // Calculate the differences between actual responses and predicted responses.
  // We must also add the intercept (parameters(0)) to the predictions.
	arma::vec temp;
	if (intercept)
	{
		// Ensure that we have the correct number of dimensions in the dataset.
		if (nRows != parameters.n_rows - 1)
		{
			Log::Fatal << "The test data must have the same number of columns as the "
					"training file." << std::endl;
		}
		temp = responses - arma::trans( (arma::trans(parameters.subvec(1,
		    parameters.n_elem - 1)) * predictors) + parameters(0));
	}
	else
	{
		// Ensure that we have the correct number of dimensions in the dataset.
		if (nRows != parameters.n_rows)
		{
			Log::Fatal << "The test data must have the same number of columns as the "
					"training file." << std::endl;
		}
		temp = responses - arma::trans((arma::trans(parameters) * predictors));
	}
  const double cost = arma::dot(temp, temp) / nCols;

  return cost;
}

std::string LinearRegression::ToString() const
{
  std::ostringstream convert;
  convert << "Linear Regression [" << this << "]" << std::endl;
  convert << "  Lambda: " << lambda << std::endl;
  return convert.str();
}
<commit_msg>Tabs to spaces.<commit_after>/**
 * @file linear_regression.cpp
 * @author James Cline
 * @author Michael Fox
 *
 * Implementation of simple linear regression.
 */
#include "linear_regression.hpp"

using namespace mlpack;
using namespace mlpack::regression;

LinearRegression::LinearRegression(const arma::mat& predictors,
                                   const arma::vec& responses,
                                   const double lambda,
                                   const bool intercept,
                                   const arma::vec& weights
                                   ) :
    lambda(lambda),
    intercept(intercept)
{
  /*
   * We want to calculate the a_i coefficients of:
   * \sum_{i=0}^n (a_i * x_i^i)
   * In order to get the intercept value, we will add a row of ones.
   */

  // We store the number of rows and columns of the predictors.
  // Reminder: Armadillo stores the data transposed from how we think of it,
  //           that is, columns are actually rows (see: column major order).
  const size_t nCols = predictors.n_cols;

  arma::mat p = predictors;
  arma::vec r = responses;
  // Here we add the row of ones to the predictors.
  // The intercept is not penalized. Add an "all ones" row to design and set
  // intercept = false to get a penalized intercept
  if(intercept)
  {
    p.insert_rows(0, arma::ones<arma::mat>(1,nCols));
  }

   if(weights.n_elem > 0)
  {
    p = p * diagmat(sqrt(weights));
    r =  sqrt(weights) % responses;
  }

  if (lambda != 0.0)
  {
    // Add the identity matrix to the predictors (this is equivalent to ridge
    // regression).  See http://math.stackexchange.com/questions/299481/ for
    // more information.
    p.insert_cols(nCols, predictors.n_rows);
    p.submat(p.n_rows - predictors.n_rows, nCols, p.n_rows - 1, nCols +
    predictors.n_rows - 1) = sqrt(lambda) * arma::eye<arma::mat>(predictors.n_rows,
        predictors.n_rows);
  }

  // We compute the QR decomposition of the predictors.
  // We transpose the predictors because they are in column major order.
  arma::mat Q, R;
  arma::qr(Q, R, arma::trans(p));

  // We compute the parameters, B, like so:
  // R * B = Q^T * responses
  // B = Q^T * responses * R^-1
  // If lambda > 0, then we must add a bunch of empty responses.
  if (lambda == 0.0)
  {
    arma::solve(parameters, R, arma::trans(Q) * r);
  }
  else
  {
    // Copy responses into larger vector.
    r.insert_rows(nCols,p.n_cols - nCols);
    arma::solve(parameters, R, arma::trans(Q) * r);
  }
}

LinearRegression::LinearRegression(const std::string& filename) :
    lambda(0.0)
{
  arma::mat parameter;
  data::Load(filename, parameter, true);
  parameters = parameter.unsafe_col(0);
}

LinearRegression::LinearRegression(const LinearRegression& linearRegression) :
    parameters(linearRegression.parameters),
    lambda(linearRegression.lambda)
{ /* Nothing to do. */ }

void LinearRegression::Predict(const arma::mat& points, arma::vec& predictions)
    const
{
  if (intercept)
  {
    // We want to be sure we have the correct number of dimensions in the dataset.
    Log::Assert(points.n_rows == parameters.n_rows-1);
    // Get the predictions, but this ignores the intercept value (parameters[0]).
    predictions = arma::trans(arma::trans(parameters.subvec(1, parameters.n_elem - 1)) * points);
    // Now add the intercept.
    predictions += parameters(0);
  }
  else
  {
    // We want to be sure we have the correct number of dimensions in the dataset.
    Log::Assert(points.n_rows == parameters.n_rows);
    predictions = arma::trans(arma::trans(parameters) * points);
  }

}

//! Compute the L2 squared error on the given predictors and responses.
double LinearRegression::ComputeError(const arma::mat& predictors,
                                      const arma::vec& responses) const
{
  // Get the number of columns and rows of the dataset.
  const size_t nCols = predictors.n_cols;
  const size_t nRows = predictors.n_rows;

  // Calculate the differences between actual responses and predicted responses.
  // We must also add the intercept (parameters(0)) to the predictions.
  arma::vec temp;
  if (intercept)
  {
    // Ensure that we have the correct number of dimensions in the dataset.
    if (nRows != parameters.n_rows - 1)
    {
      Log::Fatal << "The test data must have the same number of columns as the "
          "training file." << std::endl;
    }
    temp = responses - arma::trans( (arma::trans(parameters.subvec(1,
        parameters.n_elem - 1)) * predictors) + parameters(0));
  }
  else
  {
    // Ensure that we have the correct number of dimensions in the dataset.
    if (nRows != parameters.n_rows)
    {
      Log::Fatal << "The test data must have the same number of columns as the "
          "training file." << std::endl;
    }
    temp = responses - arma::trans((arma::trans(parameters) * predictors));
  }
  const double cost = arma::dot(temp, temp) / nCols;

  return cost;
}

std::string LinearRegression::ToString() const
{
  std::ostringstream convert;
  convert << "Linear Regression [" << this << "]" << std::endl;
  convert << "  Lambda: " << lambda << std::endl;
  return convert.str();
}
<|endoftext|>