<commit_before>/* Copyright (C) 2005-2007, Thorvald Natvig <thorvald@natvig.com>

   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   - Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright notice,
     this list of conditions and the following disclaimer in the documentation
     and/or other materials provided with the distribution.
   - Neither the name of the Mumble Developers nor the names of its
     contributors may be used to endorse or promote products derived from this
     software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "AudioInput.h"
#include "AudioOutput.h"
#include "AudioConfigDialog.h"
#include "Global.h"

static ConfigWidget *AudioConfigDialogNew(Settings &st) {
	return new AudioConfigDialog(st);
}

static ConfigRegistrar registrar(10, AudioConfigDialogNew);

AudioConfigDialog::AudioConfigDialog(Settings &st) : ConfigWidget(st) {
	QList<QString> keys;
	QString key;

	setupUi(this);

	keys=AudioInputRegistrar::qmNew->keys();
	foreach(key, keys) {
		qcbInput->addItem(key);
	}

	keys=AudioOutputRegistrar::qmNew->keys();
	foreach(key, keys) {
		qcbOutput->addItem(key);
	}

	qcbTransmit->addItem(tr("Continuous"), Settings::Continous);
	qcbTransmit->addItem(tr("Voice Activity"), Settings::VAD);
	qcbTransmit->addItem(tr("Push To Talk"), Settings::PushToTalk);

	qcbLoopback->addItem(tr("None"), Settings::None);
	qcbLoopback->addItem(tr("Local"), Settings::Local);
	qcbLoopback->addItem(tr("Server"), Settings::Server);
}

QString AudioConfigDialog::title() const {
	return tr("Basic Audio");
}

QIcon AudioConfigDialog::icon() const {
	return QIcon(QLatin1String("skin:config_basic.png"));
}

void AudioConfigDialog::load(const Settings &r) {
	int i;
	QList<QString> keys;

	keys=AudioInputRegistrar::qmNew->keys();
	i=keys.indexOf(AudioInputRegistrar::current);
	if (i >= 0)
		loadComboBox(qcbInput, i);

	keys=AudioOutputRegistrar::qmNew->keys();
	i=keys.indexOf(AudioOutputRegistrar::current);
	if (i >= 0)
		loadComboBox(qcbOutput, i);

	loadComboBox(qcbTransmit, r.atTransmit);
	loadSlider(qsTransmitHold, r.iVoiceHold);
	loadSlider(qsTransmitMin, lround(r.fVADmin * 32767.0));
	loadSlider(qsTransmitMax, lround(r.fVADmax * 32767.0));
	loadSlider(qsFrames, r.iFramesPerPacket);
	loadSlider(qsDoublePush, r.uiDoublePush / 1000);

	if (r.vsVAD == Settings::Amplitude)
		qrbAmplitude->setChecked(true);
	else
		qrbSNR->setChecked(true);

	loadCheckBox(qcbPushClick, r.bPushClick);
	loadCheckBox(qcbTCP, r.bTCPCompat);
	loadCheckBox(qcbReconnect, r.bReconnect);
	loadSlider(qsQuality, r.iQuality);
	loadSlider(qsComplexity, r.iComplexity);
	loadSlider(qsNoise, - r.iNoiseSuppress);
	loadSlider(qsAmp, 20000 - r.iMinLoudness);
	loadSlider(qsVolume, static_cast<int>(r.fVolume * 100.0));
	loadSlider(qsJitter, r.iJitterBufferSize);
	loadComboBox(qcbLoopback, r.lmLoopMode);
	loadSlider(qsPacketDelay, static_cast<int>(r.dMaxPacketDelay));
	loadSlider(qsPacketLoss, static_cast<int>(r.dPacketLoss * 100.0));
}

void AudioConfigDialog::save() const {
	s.iQuality = qsQuality->value();
	s.iNoiseSuppress = - qsNoise->value();
	s.iComplexity = qsComplexity->value();
	s.iMinLoudness = 18000 - qsAmp->value() + 2000;
	s.fVolume = qsVolume->value() / 100.0;
	s.iVoiceHold = qsTransmitHold->value();
	s.fVADmin = qsTransmitMin->value() / 32767.0;
	s.fVADmax = qsTransmitMax->value() / 32767.0;
	s.vsVAD = qrbSNR->isChecked() ? Settings::SignalToNoise : Settings::Amplitude;
	s.iFramesPerPacket = qsFrames->value();
	s.uiDoublePush = qsDoublePush->value() * 1000;
	s.bPushClick = qcbPushClick->isChecked();
	s.bTCPCompat = qcbTCP->isChecked();
	s.bReconnect = qcbReconnect->isChecked();
	s.iJitterBufferSize = qsJitter->value();
	s.atTransmit = static_cast<Settings::AudioTransmit>(qcbTransmit->currentIndex());
	s.qsAudioInput = qcbInput->currentText();
	s.qsAudioOutput = qcbOutput->currentText();
	s.lmLoopMode = static_cast<Settings::LoopMode>(qcbLoopback->currentIndex());
	s.dMaxPacketDelay = qsPacketDelay->value();
	s.dPacketLoss = qsPacketLoss->value() / 100.0;
}

bool AudioConfigDialog::expert(bool b) {
	qgbInterfaces->setVisible(b);
	qgbLoopback->setVisible(b);
	qgbOutput->setVisible(b);
	qgbAudio->setVisible(b);
	qliComplexity->setVisible(b);
	qlComplexity->setVisible(b);
	qsComplexity->setVisible(b);
	qcbTCP->setVisible(b);
	qliFrames->setVisible(b);
	qsFrames->setVisible(b);
	qlFrames->setVisible(b);
	qlTransmitHold->setVisible(b);
	qsTransmitHold->setVisible(b);
	qliTransmitHold->setVisible(b);
	qlDoublePush->setVisible(b);
	qsDoublePush->setVisible(b);
	qliDoublePush->setVisible(b);
	return true;
}

void AudioConfigDialog::on_qsFrames_valueChanged(int v) {
	qlFrames->setText(tr("%1 ms").arg(v*20));
	updateBitrate();
}

void AudioConfigDialog::on_qsDoublePush_valueChanged(int v) {
	if (v == 0)
		qlDoublePush->setText(tr("Off"));
	else
		qlDoublePush->setText(tr("%1 ms").arg(v));
}

void AudioConfigDialog::on_qsTransmitHold_valueChanged(int v) {
	double val = v * 20;
	val = val / 1000.0;
	qlTransmitHold->setText(tr("%1 s").arg(val, 0, 'f', 2));
}

void AudioConfigDialog::on_qsQuality_valueChanged(int v) {
	qlQuality->setText(QString::number(v));
	updateBitrate();
}

void AudioConfigDialog::on_qsNoise_valueChanged(int v) {
	qlNoise->setText(tr("-%1 dB").arg(v));
}

void AudioConfigDialog::on_qsComplexity_valueChanged(int v) {
	qlComplexity->setText(QString::number(v));
}

void AudioConfigDialog::on_qsAmp_valueChanged(int v) {
	v = 18000 - v + 2000;
	double d = 20000.0/v;
	qlAmp->setText(QString::fromLatin1("%1").arg(d, 0, 'f', 2));
}

void AudioConfigDialog::on_qsJitter_valueChanged(int v) {
	qlJitter->setText(tr("%1 ms").arg(v*20));
}

void AudioConfigDialog::on_qsVolume_valueChanged(int v) {
	qlVolume->setText(tr("%1%").arg(v));
}

void AudioConfigDialog::on_qsPacketDelay_valueChanged(int v) {
	qlPacketDelay->setText(tr("%1 ms").arg(v));
}

void AudioConfigDialog::on_qsPacketLoss_valueChanged(int v) {
	qlPacketLoss->setText(tr("%1%").arg(v));
}

void AudioConfigDialog::updateBitrate() {
	if (! qsQuality || ! qsFrames || ! qlBitrate)
		return;
	int q = qsQuality->value();
	int p = qsFrames->value();

	int audiorate, overhead, posrate;
	float f = q;
	void *es;

	es = speex_encoder_init(&speex_wb_mode);
	speex_encoder_ctl(es,SPEEX_SET_VBR_QUALITY, &f);
	speex_encoder_ctl(es,SPEEX_GET_BITRATE,&audiorate);
	speex_encoder_destroy(es);

	// 50 packets, in bits, IP + UDP + Crypt + type/id (Message header) + flags + seq
	overhead = 50 * 8 * (20 + 8 + 4 + 3 + 1 + 2);

	// TCP is 12 more bytes than UDP
	if (qcbTCP->isChecked())
		overhead += 50 * 8 * 12;

	if (g.s.bTransmitPosition)
		posrate = 12;
	else
		posrate = 0;

	posrate = posrate * 50 * 8;

	overhead = overhead / p;
	posrate = posrate / p;

	int total = audiorate + overhead + posrate;

	QPalette pal;

	if ((total / 8 > g.iMaxBandwidth) && g.uiSession) {
		pal.setColor(qlBitrate->foregroundRole(), Qt::red);
	}

	qlBitrate->setPalette(pal);

	QString v = tr("%1kbit/s (Audio %2, Position %4, Overhead %3)").arg(total / 1000.0, 0, 'f', 1).arg(audiorate / 1000.0, 0, 'f', 1).arg(overhead / 1000.0, 0, 'f', 1).arg(posrate / 1000.0, 0, 'f', 1);
	qlBitrate->setText(v);
}

void AudioConfigDialog::on_qcbTransmit_currentIndexChanged(int v) {

	bool cue = false;
	bool hold = false;

	switch (v) {
		case 0:
			qswTransmit->setCurrentWidget(qwContinuous);
			break;
		case 1:
			qswTransmit->setCurrentWidget(qwVAD);
			break;
		case 2:
			qswTransmit->setCurrentWidget(qwPTT);
			cue = true;
			break;
	}
}

void AudioConfigDialog::on_qcbLoopback_currentIndexChanged(int v) {
	bool ena = false;
	if (v == 1)
		ena = true;

	qsPacketDelay->setEnabled(ena);
	qlPacketDelay->setEnabled(ena);
	qsPacketLoss->setEnabled(ena);
	qlPacketLoss->setEnabled(ena);
}
<commit_msg>Cleanup for non-expert mode<commit_after>/* Copyright (C) 2005-2007, Thorvald Natvig <thorvald@natvig.com>

   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   - Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright notice,
     this list of conditions and the following disclaimer in the documentation
     and/or other materials provided with the distribution.
   - Neither the name of the Mumble Developers nor the names of its
     contributors may be used to endorse or promote products derived from this
     software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "AudioInput.h"
#include "AudioOutput.h"
#include "AudioConfigDialog.h"
#include "Global.h"

static ConfigWidget *AudioConfigDialogNew(Settings &st) {
	return new AudioConfigDialog(st);
}

static ConfigRegistrar registrar(10, AudioConfigDialogNew);

AudioConfigDialog::AudioConfigDialog(Settings &st) : ConfigWidget(st) {
	QList<QString> keys;
	QString key;

	setupUi(this);

	keys=AudioInputRegistrar::qmNew->keys();
	foreach(key, keys) {
		qcbInput->addItem(key);
	}

	keys=AudioOutputRegistrar::qmNew->keys();
	foreach(key, keys) {
		qcbOutput->addItem(key);
	}

	qcbTransmit->addItem(tr("Continuous"), Settings::Continous);
	qcbTransmit->addItem(tr("Voice Activity"), Settings::VAD);
	qcbTransmit->addItem(tr("Push To Talk"), Settings::PushToTalk);

	qcbLoopback->addItem(tr("None"), Settings::None);
	qcbLoopback->addItem(tr("Local"), Settings::Local);
	qcbLoopback->addItem(tr("Server"), Settings::Server);
}

QString AudioConfigDialog::title() const {
	return tr("Basic Audio");
}

QIcon AudioConfigDialog::icon() const {
	return QIcon(QLatin1String("skin:config_basic.png"));
}

void AudioConfigDialog::load(const Settings &r) {
	int i;
	QList<QString> keys;

	keys=AudioInputRegistrar::qmNew->keys();
	i=keys.indexOf(AudioInputRegistrar::current);
	if (i >= 0)
		loadComboBox(qcbInput, i);

	keys=AudioOutputRegistrar::qmNew->keys();
	i=keys.indexOf(AudioOutputRegistrar::current);
	if (i >= 0)
		loadComboBox(qcbOutput, i);

	loadComboBox(qcbTransmit, r.atTransmit);
	loadSlider(qsTransmitHold, r.iVoiceHold);
	loadSlider(qsTransmitMin, lround(r.fVADmin * 32767.0));
	loadSlider(qsTransmitMax, lround(r.fVADmax * 32767.0));
	loadSlider(qsFrames, r.iFramesPerPacket);
	loadSlider(qsDoublePush, r.uiDoublePush / 1000);

	if (r.vsVAD == Settings::Amplitude)
		qrbAmplitude->setChecked(true);
	else
		qrbSNR->setChecked(true);

	loadCheckBox(qcbPushClick, r.bPushClick);
	loadCheckBox(qcbTCP, r.bTCPCompat);
	loadCheckBox(qcbReconnect, r.bReconnect);
	loadSlider(qsQuality, r.iQuality);
	loadSlider(qsComplexity, r.iComplexity);
	loadSlider(qsNoise, - r.iNoiseSuppress);
	loadSlider(qsAmp, 20000 - r.iMinLoudness);
	loadSlider(qsVolume, static_cast<int>(r.fVolume * 100.0));
	loadSlider(qsJitter, r.iJitterBufferSize);
	loadComboBox(qcbLoopback, r.lmLoopMode);
	loadSlider(qsPacketDelay, static_cast<int>(r.dMaxPacketDelay));
	loadSlider(qsPacketLoss, static_cast<int>(r.dPacketLoss * 100.0));
}

void AudioConfigDialog::save() const {
	s.iQuality = qsQuality->value();
	s.iNoiseSuppress = - qsNoise->value();
	s.iComplexity = qsComplexity->value();
	s.iMinLoudness = 18000 - qsAmp->value() + 2000;
	s.fVolume = qsVolume->value() / 100.0;
	s.iVoiceHold = qsTransmitHold->value();
	s.fVADmin = qsTransmitMin->value() / 32767.0;
	s.fVADmax = qsTransmitMax->value() / 32767.0;
	s.vsVAD = qrbSNR->isChecked() ? Settings::SignalToNoise : Settings::Amplitude;
	s.iFramesPerPacket = qsFrames->value();
	s.uiDoublePush = qsDoublePush->value() * 1000;
	s.bPushClick = qcbPushClick->isChecked();
	s.bTCPCompat = qcbTCP->isChecked();
	s.bReconnect = qcbReconnect->isChecked();
	s.iJitterBufferSize = qsJitter->value();
	s.atTransmit = static_cast<Settings::AudioTransmit>(qcbTransmit->currentIndex());
	s.qsAudioInput = qcbInput->currentText();
	s.qsAudioOutput = qcbOutput->currentText();
	s.lmLoopMode = static_cast<Settings::LoopMode>(qcbLoopback->currentIndex());
	s.dMaxPacketDelay = qsPacketDelay->value();
	s.dPacketLoss = qsPacketLoss->value() / 100.0;
}

bool AudioConfigDialog::expert(bool b) {
	qgbInterfaces->setVisible(b);
	qgbLoopback->setVisible(b);
	qgbOutput->setVisible(b);
	qgbAudio->setVisible(b);
	qliComplexity->setVisible(b);
	qlComplexity->setVisible(b);
	qsComplexity->setVisible(b);
	qcbTCP->setVisible(b);
	qliFrames->setVisible(b);
	qsFrames->setVisible(b);
	qlFrames->setVisible(b);
	qswTransmit->setVisible(b);
	return true;
}

void AudioConfigDialog::on_qsFrames_valueChanged(int v) {
	qlFrames->setText(tr("%1 ms").arg(v*20));
	updateBitrate();
}

void AudioConfigDialog::on_qsDoublePush_valueChanged(int v) {
	if (v == 0)
		qlDoublePush->setText(tr("Off"));
	else
		qlDoublePush->setText(tr("%1 ms").arg(v));
}

void AudioConfigDialog::on_qsTransmitHold_valueChanged(int v) {
	double val = v * 20;
	val = val / 1000.0;
	qlTransmitHold->setText(tr("%1 s").arg(val, 0, 'f', 2));
}

void AudioConfigDialog::on_qsQuality_valueChanged(int v) {
	qlQuality->setText(QString::number(v));
	updateBitrate();
}

void AudioConfigDialog::on_qsNoise_valueChanged(int v) {
	qlNoise->setText(tr("-%1 dB").arg(v));
}

void AudioConfigDialog::on_qsComplexity_valueChanged(int v) {
	qlComplexity->setText(QString::number(v));
}

void AudioConfigDialog::on_qsAmp_valueChanged(int v) {
	v = 18000 - v + 2000;
	double d = 20000.0/v;
	qlAmp->setText(QString::fromLatin1("%1").arg(d, 0, 'f', 2));
}

void AudioConfigDialog::on_qsJitter_valueChanged(int v) {
	qlJitter->setText(tr("%1 ms").arg(v*20));
}

void AudioConfigDialog::on_qsVolume_valueChanged(int v) {
	qlVolume->setText(tr("%1%").arg(v));
}

void AudioConfigDialog::on_qsPacketDelay_valueChanged(int v) {
	qlPacketDelay->setText(tr("%1 ms").arg(v));
}

void AudioConfigDialog::on_qsPacketLoss_valueChanged(int v) {
	qlPacketLoss->setText(tr("%1%").arg(v));
}

void AudioConfigDialog::updateBitrate() {
	if (! qsQuality || ! qsFrames || ! qlBitrate)
		return;
	int q = qsQuality->value();
	int p = qsFrames->value();

	int audiorate, overhead, posrate;
	float f = q;
	void *es;

	es = speex_encoder_init(&speex_wb_mode);
	speex_encoder_ctl(es,SPEEX_SET_VBR_QUALITY, &f);
	speex_encoder_ctl(es,SPEEX_GET_BITRATE,&audiorate);
	speex_encoder_destroy(es);

	// 50 packets, in bits, IP + UDP + Crypt + type/id (Message header) + flags + seq
	overhead = 50 * 8 * (20 + 8 + 4 + 3 + 1 + 2);

	// TCP is 12 more bytes than UDP
	if (qcbTCP->isChecked())
		overhead += 50 * 8 * 12;

	if (g.s.bTransmitPosition)
		posrate = 12;
	else
		posrate = 0;

	posrate = posrate * 50 * 8;

	overhead = overhead / p;
	posrate = posrate / p;

	int total = audiorate + overhead + posrate;

	QPalette pal;

	if ((total / 8 > g.iMaxBandwidth) && g.uiSession) {
		pal.setColor(qlBitrate->foregroundRole(), Qt::red);
	}

	qlBitrate->setPalette(pal);

	QString v = tr("%1kbit/s (Audio %2, Position %4, Overhead %3)").arg(total / 1000.0, 0, 'f', 1).arg(audiorate / 1000.0, 0, 'f', 1).arg(overhead / 1000.0, 0, 'f', 1).arg(posrate / 1000.0, 0, 'f', 1);
	qlBitrate->setText(v);
}

void AudioConfigDialog::on_qcbTransmit_currentIndexChanged(int v) {
	switch (v) {
		case 0:
			qswTransmit->setCurrentWidget(qwContinuous);
			break;
		case 1:
			qswTransmit->setCurrentWidget(qwVAD);
			break;
		case 2:
			qswTransmit->setCurrentWidget(qwPTT);
			break;
	}
}

void AudioConfigDialog::on_qcbLoopback_currentIndexChanged(int v) {
	bool ena = false;
	if (v == 1)
		ena = true;

	qsPacketDelay->setEnabled(ena);
	qlPacketDelay->setEnabled(ena);
	qsPacketLoss->setEnabled(ena);
	qlPacketLoss->setEnabled(ena);
}
<|endoftext|>


<commit_before>/*++
Copyright (c) 2013 Microsoft Corporation

Module Name:

    rule_properties.cpp

Abstract:

    Collect properties of rules.

Author:

    Nikolaj Bjorner (nbjorner) 9-25-2014

Notes:

--*/

#include"expr_functors.h"
#include"rule_properties.h"
#include"dl_rule_set.h"
#include"for_each_expr.h"
#include"dl_context.h"

using namespace datalog;
rule_properties::rule_properties(ast_manager & m, rule_manager& rm, context& ctx, i_expr_pred& p):
    m(m), rm(rm), m_ctx(ctx), m_is_predicate(p), m_dt(m), m_dl(m), m_bv(m), m_generate_proof(false) {}

rule_properties::~rule_properties() {}

void rule_properties::collect(rule_set const& rules) {
    reset();
    rule_set::iterator it = rules.begin(), end = rules.end();
    expr_sparse_mark visited;
    for (; it != end; ++it) {
        rule* r = *it;
        m_rule = r;
        unsigned ut_size = r->get_uninterpreted_tail_size();
        unsigned t_size  = r->get_tail_size();
        if (r->has_negation()) {
            m_negative_rules.push_back(r);
        }
        for (unsigned i = ut_size; i < t_size; ++i) {
            for_each_expr_core<rule_properties,expr_sparse_mark, true, false>(*this, visited, r->get_tail(i));
        }
        if (m_generate_proof && !r->get_proof()) {
            rm.mk_rule_asserted_proof(*r);
        }
        for (unsigned i = 0; m_inf_sort.empty() && i < r->get_decl()->get_arity(); ++i) {
            sort* d = r->get_decl()->get_domain(i);
            if (!m.is_bool(d) && !m_dl.is_finite_sort(d) && !m_bv.is_bv_sort(d)) {
                m_inf_sort.push_back(m_rule);
            }
        }
    }
}

void rule_properties::check_quantifier_free() {
    if (!m_quantifiers.empty()) {
        rule* r = m_quantifiers.begin()->m_value;
        std::stringstream stm;
        stm << "cannot process quantifier in rule ";
        r->display(m_ctx, stm);
        throw default_exception(stm.str());
    }
}

void rule_properties::check_for_negated_predicates() {
    if (!m_negative_rules.empty()) {
        rule* r = m_negative_rules[0];
        std::stringstream stm;
        stm << "Rule contains negative predicate ";
        r->display(m_ctx, stm);
        throw default_exception(stm.str());
    }
}

void rule_properties::check_uninterpreted_free() {
    if (!m_uninterp_funs.empty()) {
        func_decl* f = m_uninterp_funs.begin()->m_key;
        rule* r = m_uninterp_funs.begin()->m_value;
        std::stringstream stm;
        stm << "Uninterpreted '"
            << f->get_name()
            << "' in ";
        r->display(m_ctx, stm);
        throw default_exception(stm.str());
    }
}

void rule_properties::check_infinite_sorts() {
    if (!m_inf_sort.empty()) {
        std::stringstream stm;
        rule* r = m_inf_sort.back();
        stm << "Rule contains infinite sorts in rule ";
        r->display(m_ctx, stm);
        throw default_exception(stm.str());
    }
}

void rule_properties::check_nested_free() {
    if (!m_interp_pred.empty()) {
        std::stringstream stm;
        rule* r = m_interp_pred[0];
        stm << "Rule contains nested predicates ";
        r->display(m_ctx, stm);
        throw default_exception(stm.str());
    }
}

void rule_properties::check_existential_tail() {
    ast_mark visited;
    ptr_vector<expr> todo, tocheck;
    for (unsigned i = 0; i < m_interp_pred.size(); ++i) {
        rule& r = *m_interp_pred[i];
        unsigned ut_size = r.get_uninterpreted_tail_size();
        unsigned t_size  = r.get_tail_size();
        for (unsigned i = ut_size; i < t_size; ++i) {
            todo.push_back(r.get_tail(i));
        }
    }
    context::contains_pred contains_p(m_ctx);
    check_pred check_pred(contains_p, m);

    while (!todo.empty()) {
        expr* e = todo.back(), *e1, *e2;
        todo.pop_back();
        if (visited.is_marked(e)) {
            continue;
        }
        visited.mark(e, true);
        if (m_is_predicate(e)) {
        }
        else if (m.is_and(e) || m.is_or(e)) {
            todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
        }
        else if (m.is_implies(e, e1, e2)) {
            tocheck.push_back(e1);
            todo.push_back(e2);
        }
        else if (is_quantifier(e)) {
            todo.push_back(to_quantifier(e)->get_expr());
        }
        else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) &&
                 m.is_true(e1)) {
            todo.push_back(e2);
        }
        else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) &&
                 m.is_true(e2)) {
            todo.push_back(e1);
        }
        else {
            tocheck.push_back(e);
        }
    }
    for (unsigned i = 0; i < tocheck.size(); ++i) {
        expr* e = tocheck[i];
        if (check_pred(e)) {
            std::ostringstream out;
            out << "recursive predicate " << mk_ismt2_pp(e, m) << " occurs nested in the body of a rule";
            throw default_exception(out.str());
        }
    }
}

void rule_properties::insert(ptr_vector<rule>& rules, rule* r) {
    if (rules.empty() || rules.back() != r) {
        rules.push_back(r);
    }
}

void rule_properties::operator()(var* n) {
}

void rule_properties::operator()(quantifier* n) {
    m_quantifiers.insert(n, m_rule);
}
void rule_properties::operator()(app* n) {
    if (m_is_predicate(n)) {
        insert(m_interp_pred, m_rule);
    }
    else if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) {
        m_uninterp_funs.insert(n->get_decl(), m_rule);
    }
    else if (m_dt.is_accessor(n)) {
        sort* s = m.get_sort(n->get_arg(0));
        SASSERT(m_dt.is_datatype(s));
        if (m_dt.get_datatype_constructors(s)->size() > 1) {
            m_uninterp_funs.insert(n->get_decl(), m_rule);
        }
    }
    else {
        std::cout << mk_pp(n, m) << "\n";
    }

}

void rule_properties::reset() {
    m_quantifiers.reset();
    m_uninterp_funs.reset();
    m_interp_pred.reset();
    m_negative_rules.reset();
    m_inf_sort.reset();
}

<commit_msg>remove std-out<commit_after>/*++
Copyright (c) 2013 Microsoft Corporation

Module Name:

    rule_properties.cpp

Abstract:

    Collect properties of rules.

Author:

    Nikolaj Bjorner (nbjorner) 9-25-2014

Notes:

--*/

#include"expr_functors.h"
#include"rule_properties.h"
#include"dl_rule_set.h"
#include"for_each_expr.h"
#include"dl_context.h"

using namespace datalog;
rule_properties::rule_properties(ast_manager & m, rule_manager& rm, context& ctx, i_expr_pred& p):
    m(m), rm(rm), m_ctx(ctx), m_is_predicate(p), m_dt(m), m_dl(m), m_bv(m), m_generate_proof(false) {}

rule_properties::~rule_properties() {}

void rule_properties::collect(rule_set const& rules) {
    reset();
    rule_set::iterator it = rules.begin(), end = rules.end();
    expr_sparse_mark visited;
    for (; it != end; ++it) {
        rule* r = *it;
        m_rule = r;
        unsigned ut_size = r->get_uninterpreted_tail_size();
        unsigned t_size  = r->get_tail_size();
        if (r->has_negation()) {
            m_negative_rules.push_back(r);
        }
        for (unsigned i = ut_size; i < t_size; ++i) {
            for_each_expr_core<rule_properties,expr_sparse_mark, true, false>(*this, visited, r->get_tail(i));
        }
        if (m_generate_proof && !r->get_proof()) {
            rm.mk_rule_asserted_proof(*r);
        }
        for (unsigned i = 0; m_inf_sort.empty() && i < r->get_decl()->get_arity(); ++i) {
            sort* d = r->get_decl()->get_domain(i);
            if (!m.is_bool(d) && !m_dl.is_finite_sort(d) && !m_bv.is_bv_sort(d)) {
                m_inf_sort.push_back(m_rule);
            }
        }
    }
}

void rule_properties::check_quantifier_free() {
    if (!m_quantifiers.empty()) {
        rule* r = m_quantifiers.begin()->m_value;
        std::stringstream stm;
        stm << "cannot process quantifier in rule ";
        r->display(m_ctx, stm);
        throw default_exception(stm.str());
    }
}

void rule_properties::check_for_negated_predicates() {
    if (!m_negative_rules.empty()) {
        rule* r = m_negative_rules[0];
        std::stringstream stm;
        stm << "Rule contains negative predicate ";
        r->display(m_ctx, stm);
        throw default_exception(stm.str());
    }
}

void rule_properties::check_uninterpreted_free() {
    if (!m_uninterp_funs.empty()) {
        func_decl* f = m_uninterp_funs.begin()->m_key;
        rule* r = m_uninterp_funs.begin()->m_value;
        std::stringstream stm;
        stm << "Uninterpreted '"
            << f->get_name()
            << "' in ";
        r->display(m_ctx, stm);
        throw default_exception(stm.str());
    }
}

void rule_properties::check_infinite_sorts() {
    if (!m_inf_sort.empty()) {
        std::stringstream stm;
        rule* r = m_inf_sort.back();
        stm << "Rule contains infinite sorts in rule ";
        r->display(m_ctx, stm);
        throw default_exception(stm.str());
    }
}

void rule_properties::check_nested_free() {
    if (!m_interp_pred.empty()) {
        std::stringstream stm;
        rule* r = m_interp_pred[0];
        stm << "Rule contains nested predicates ";
        r->display(m_ctx, stm);
        throw default_exception(stm.str());
    }
}

void rule_properties::check_existential_tail() {
    ast_mark visited;
    ptr_vector<expr> todo, tocheck;
    for (unsigned i = 0; i < m_interp_pred.size(); ++i) {
        rule& r = *m_interp_pred[i];
        unsigned ut_size = r.get_uninterpreted_tail_size();
        unsigned t_size  = r.get_tail_size();
        for (unsigned i = ut_size; i < t_size; ++i) {
            todo.push_back(r.get_tail(i));
        }
    }
    context::contains_pred contains_p(m_ctx);
    check_pred check_pred(contains_p, m);

    while (!todo.empty()) {
        expr* e = todo.back(), *e1, *e2;
        todo.pop_back();
        if (visited.is_marked(e)) {
            continue;
        }
        visited.mark(e, true);
        if (m_is_predicate(e)) {
        }
        else if (m.is_and(e) || m.is_or(e)) {
            todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
        }
        else if (m.is_implies(e, e1, e2)) {
            tocheck.push_back(e1);
            todo.push_back(e2);
        }
        else if (is_quantifier(e)) {
            todo.push_back(to_quantifier(e)->get_expr());
        }
        else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) &&
                 m.is_true(e1)) {
            todo.push_back(e2);
        }
        else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) &&
                 m.is_true(e2)) {
            todo.push_back(e1);
        }
        else {
            tocheck.push_back(e);
        }
    }
    for (unsigned i = 0; i < tocheck.size(); ++i) {
        expr* e = tocheck[i];
        if (check_pred(e)) {
            std::ostringstream out;
            out << "recursive predicate " << mk_ismt2_pp(e, m) << " occurs nested in the body of a rule";
            throw default_exception(out.str());
        }
    }
}

void rule_properties::insert(ptr_vector<rule>& rules, rule* r) {
    if (rules.empty() || rules.back() != r) {
        rules.push_back(r);
    }
}

void rule_properties::operator()(var* n) {
}

void rule_properties::operator()(quantifier* n) {
    m_quantifiers.insert(n, m_rule);
}
void rule_properties::operator()(app* n) {
    if (m_is_predicate(n)) {
        insert(m_interp_pred, m_rule);
    }
    else if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) {
        m_uninterp_funs.insert(n->get_decl(), m_rule);
    }
    else if (m_dt.is_accessor(n)) {
        sort* s = m.get_sort(n->get_arg(0));
        SASSERT(m_dt.is_datatype(s));
        if (m_dt.get_datatype_constructors(s)->size() > 1) {
            m_uninterp_funs.insert(n->get_decl(), m_rule);
        }
    }
    else {
    }

}

void rule_properties::reset() {
    m_quantifiers.reset();
    m_uninterp_funs.reset();
    m_interp_pred.reset();
    m_negative_rules.reset();
    m_inf_sort.reset();
}

<|endoftext|>


<commit_before>/*
 * DIPlib 3.0
 * This file contains the area opening and related functions.
 *
 * (c)2008, 2017, Cris Luengo.
 *
 * 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 "diplib.h"
#include "diplib/morphology.h"
#include "diplib/binary.h"
#include "diplib/neighborlist.h"
#include "diplib/iterators.h"
#include "diplib/boundary.h"
#include "diplib/overload.h"
#include "diplib/union_find.h"
#include "watershed_support.h"

namespace dip {

namespace {

/*
The code below is very similar to that for FastWatershed, but instead of recording the value of the local
minimum/maximum in each watershed basin, we record the grey value at which the size criterion was met.
This value can then be used to paint all pixels that are larger/smaller within the basin.
*/

template< typename TPI >
struct AreaOpenRegion {
   dip::uint size = 0;
   TPI lowest = 0;

   AreaOpenRegion() = default;
   explicit AreaOpenRegion( TPI value ) : size( 1 ), lowest( value ) {}

   dip::uint Param() const {
      return size;
   }

   void Saturate( dip::uint filterSize ) {
      size = filterSize;
   }

   void AddRegionSize( AreaOpenRegion const& other ) {
      size += other.size - 1; // Any time we use this operator, the current pixel has already been added to both regions
   }

   void AddPixel( TPI value, dip::uint filterSize ) {
      if( size < filterSize ) {
         ++size;
         lowest = value;
      }
   }
};

/*
For the volume opening, we do the same thing but track the volume of the peak instead of its area.
*/

template< typename TPI >
struct VolumeOpenRegion {
   dip::uint size = 0;
   dfloat volume = 0;
   TPI lowest = 0;

   VolumeOpenRegion() = default;
   explicit VolumeOpenRegion( TPI value ) : size( 1 ), lowest( value ) {}

   dfloat Param() const {
      return volume;
   }

   void Saturate( dfloat filterSize ) {
      volume = filterSize;
   }

   void AddRegionSize( VolumeOpenRegion const& other ) {
      size += other.size - 1; // Any time we use this operator, the current pixel has already been added to both regions, so there's no need to update `lowest`.
      volume += other.volume;
   }

   void AddPixel( TPI value, dfloat filterSize ) {
      if( volume < filterSize ) { // let's compute only if necessary
         dfloat height = std::abs( static_cast< dfloat >( lowest ) - static_cast< dfloat >( value ));
         if(( volume + static_cast< dfloat >( size ) * height ) < filterSize ) {
            // Adding the full height will not make this region larger than the filter size
            volume += static_cast< dfloat >( size ) * height;
            lowest = value;
         } else {
            // We need to add a smaller height to get the exact filter size.
            // But actually, the height is a little less than the exact result, so that our volume remains strictly less than `filterSize`.
            height = ( filterSize - volume ) / static_cast< dfloat >( size ) * ( 1 - 1e-6 );
            if( lowest > value ) {
               height = -height; // we want to subtract from `lowest`, rather than add, to move towards `value`.
            }
            volume = filterSize; // equal to `volume += static_cast< dfloat >( size ) * height` but without rounding errors.
            lowest = static_cast< TPI >( lowest + static_cast< TPI >( height )); // casting to integer will always round towards zero, which is just what we want to do here.
         }
         ++size;
      }
   }
};

template< typename TPI, typename RegionType >
RegionType AddRegions( RegionType region1, RegionType const& region2 ) {
   // When we get here, we've already added the current pixel to both regions, so the `lowest` value should be the same.
   DIP_ASSERT( region1.lowest == region2.lowest );
   region1.AddRegionSize( region2 );
   return region1;
}

template< typename UnionFunction, typename RegionType >
using ParametricOpenRegionList = UnionFind< LabelType, RegionType, UnionFunction >;

template< typename TPI, typename RegionType, typename ParamType >
void ParametricOpeningInternal(
      Image& c_grey,
      Image& c_labels,
      std::vector< dip::sint > const& offsets,
      IntegerArray const& neighborOffsets,
      ParamType filterSize,
      bool lowFirst
) {
   TPI* grey = static_cast< TPI* >( c_grey.Origin() );
   LabelType* labels = static_cast< LabelType* >( c_labels.Origin() );

   auto addRegionsFnc = AddRegions< TPI, RegionType >;
   ParametricOpenRegionList< decltype( addRegionsFnc ), RegionType > regions( addRegionsFnc );
   NeighborLabels neighborLabels;

   // Process first pixel
   labels[ offsets[ 0 ]] = regions.Create( RegionType( grey[ offsets[ 0 ]] ));

   // Process other pixels
   for( dip::uint ii = 1; ii < offsets.size(); ++ii ) {
      dip::sint offset = offsets[ ii ];
      if( lowFirst ? PixelIsInfinity( grey[ offset ] ) : PixelIsMinusInfinity( grey[ offset ] )) {
         break; // we're done
      }
      neighborLabels.Reset();
      for( auto o : neighborOffsets ) {
         neighborLabels.Push( regions.FindRoot( labels[ offset + o ] ));
      }
      if( neighborLabels.Size() == 0 ) {
         // Not touching a label: new label
         labels[ offset ] = regions.Create( RegionType( grey[ offset ] ));
      } else if( neighborLabels.Size() == 1 ) {
         // Touching a single label: grow
         LabelType lab = neighborLabels.Label( 0 );
         labels[ offset ] = lab;
         regions.Value( lab ).AddPixel( grey[ offset ], filterSize );
      } else {
         // Touching two or more labels
         // Grow each of the small regions, and find the label of the smallest region
         LabelType lab = neighborLabels.Label( 0 );
         ParamType size = regions.Value( lab ).Param();
         for( auto lab2 : neighborLabels ) {
            if( regions.Value( lab2 ).Param() < size ) {
               lab = lab2;
               size = regions.Value( lab ).Param();
            }
            regions.Value( lab2 ).AddPixel( grey[ offset ], filterSize );
         }
         // Assign the pixel to the smallest region
         labels[ offset ] = lab;
         if( regions.Value( lab ).Param() < filterSize ) {
            // If the region is still small, combine information from the other regions
            for( LabelType lab2 : neighborLabels ) {
               if( lab != lab2 ) {
                  if(( regions.Value( lab ).Param() + regions.Value( lab2 ).Param() ) < filterSize ) {
                     regions.Union( lab, lab2 );
                  } else {
                     // If we don't merge, both regions should stop growing
                     regions.Value( lab ).Saturate( filterSize );
                     regions.Value( lab2 ).Saturate( filterSize );
                  }
               }
            }
         }
      }

   }
   JointImageIterator< TPI, LabelType > it( { c_grey, c_labels } );
   it.OptimizeAndFlatten();
   if( lowFirst ) {
      do {
         LabelType lab = it.template Sample< 1 >();
         if( lab > 0 ) {
            TPI v = regions.Value( lab ).lowest;
            if( it.template Sample< 0 >() < v ) {
               it.template Sample< 0 >() = v;
            }
         }
      } while( ++it );
   } else {
      do {
         LabelType lab = it.template Sample< 1 >();
         if( lab > 0 ) {
            TPI v = regions.Value( lab ).lowest;
            if( it.template Sample< 0 >() > v ) {
               it.template Sample< 0 >() = v;
            }
         }
      } while( ++it );
   }
}

template< typename TPI >
void AreaOpeningInternal(
      Image& grey,
      Image& labels,
      std::vector< dip::sint > const& offsets,
      IntegerArray const& neighborOffsets,
      dip::uint filterSize,
      bool lowFirst
) {
   ParametricOpeningInternal< TPI, AreaOpenRegion< TPI >>( grey, labels, offsets, neighborOffsets, filterSize, lowFirst );
}

template< typename TPI >
void VolumeOpeningInternal(
      Image& grey,
      Image& labels,
      std::vector< dip::sint > const& offsets,
      IntegerArray const& neighborOffsets,
      dfloat filterSize,
      bool lowFirst
) {
   ParametricOpeningInternal< TPI, VolumeOpenRegion< TPI >>( grey, labels, offsets, neighborOffsets, filterSize, lowFirst );
}

enum class ParametricOpeningMode {
      AREA_OPENING,
      VOLUME_OPENING
};

void ParametricOpening(
      Image const& c_in,
      Image const& c_mask,
      Image& out,
      void* filterSizePtr, // Points to either a dip::uint or a dip::dfloat, depending on `mode`. Sorry, don't know how else to do this nicely without a `constexpr if`. TODO: Upgrade to C++17?
      dip::uint connectivity,
      String const& polarity,
      ParametricOpeningMode mode
) {
   // Check input
   DIP_THROW_IF( !c_in.IsForged(), E::IMAGE_NOT_FORGED );
   DIP_THROW_IF( !c_in.IsScalar(), E::IMAGE_NOT_SCALAR );
   DIP_THROW_IF( !c_in.DataType().IsReal(), E::DATA_TYPE_NOT_SUPPORTED );
   dip::uint nDims = c_in.Dimensionality();
   DIP_THROW_IF( nDims < 1, E::DIMENSIONALITY_NOT_SUPPORTED );
   DIP_THROW_IF( connectivity > nDims, E::ILLEGAL_CONNECTIVITY );
   bool lowFirst;
   DIP_STACK_TRACE_THIS( lowFirst = BooleanFromString( polarity, S::CLOSING, S::OPENING ));

   // Add a 1-pixel boundary around the input image
   Image grey;
   ExtendImage( c_in, grey, { 1 }, { lowFirst ? BoundaryCondition::ADD_MAX_VALUE : BoundaryCondition::ADD_MIN_VALUE } );

   // Prepare labels image
   Image labels;
   labels.SetStrides( grey.Strides() );
   labels.SetSizes( grey.Sizes() );
   labels.SetDataType( DT_LABEL );
   labels.Forge();
   DIP_ASSERT( labels.Strides() == grey.Strides() );
   labels.Fill( 0 );

   // Check mask, expand mask singleton dimensions if necessary
   Image mask;
   bool hasMask = false;
   if( c_mask.IsForged() ) {
      mask = c_mask.QuickCopy();
      UnsignedArray const& inSizes = c_in.Sizes();
      DIP_START_STACK_TRACE
         mask.CheckIsMask( inSizes, Option::AllowSingletonExpansion::DO_ALLOW, Option::ThrowException::DO_THROW );
         mask.ExpandSingletonDimensions( inSizes );
      DIP_END_STACK_TRACE
      hasMask = true;
   }

   // Create sorted offsets array (skipping border)
   std::vector< dip::sint > offsets;
   if( hasMask ) {
      offsets = CreateOffsetsArray( mask, grey.Strides() );
      // The mask is shifted by one pixel, because we added a pixel to `grey`. Add the offset to the offsets!
      UnsignedArray pos( nDims, 1 );
      dip::sint firstPixelOffset = grey.Offset( pos );
      for( auto& o : offsets ) {
         o += firstPixelOffset;
      }
   } else {
      offsets = CreateOffsetsArray( grey.Sizes(), grey.Strides() );
   }
   if( offsets.empty() ) {
      // This can happen if `mask` is empty.
      return;
   }
   SortOffsets( grey, offsets, lowFirst );

   // Create array with offsets to neighbors
   NeighborList neighbors( { Metric::TypeCode::CONNECTED, connectivity }, nDims );
   IntegerArray neighborOffsets = neighbors.ComputeOffsets( grey.Strides() );

   // Do the data-type-dependent thing
   DIP_START_STACK_TRACE
      if( mode == ParametricOpeningMode::AREA_OPENING ) {
         DIP_OVL_CALL_REAL( AreaOpeningInternal, ( grey, labels, offsets, neighborOffsets, *static_cast< dip::uint* >( filterSizePtr ), lowFirst ), grey.DataType());
      } else {
         DIP_OVL_CALL_REAL( VolumeOpeningInternal, ( grey, labels, offsets, neighborOffsets, *static_cast< dfloat* >( filterSizePtr ), lowFirst ), grey.DataType());
      }
   DIP_END_STACK_TRACE

   // Copy result to output
   grey.Crop( c_in.Sizes() );
   PixelSize pixelSize = c_in.PixelSize();
   out.Copy( grey );
   out.SetPixelSize( std::move( pixelSize ));
}

} // namespace

void AreaOpening(
      Image const& in,
      Image const& mask,
      Image& out,
      dip::uint filterSize,
      dip::uint connectivity,
      String const& polarity
) {
   bool lowFirst;
   DIP_STACK_TRACE_THIS( lowFirst = BooleanFromString( polarity, S::CLOSING, S::OPENING ));
   if( in.DataType().IsBinary() ) {
      DIP_START_STACK_TRACE
         if( lowFirst ) {
            BinaryAreaClosing( in, out, filterSize, connectivity );
         } else {
            BinaryAreaOpening( in, out, filterSize, connectivity );
         }
      DIP_END_STACK_TRACE
      return;
   }
   DIP_STACK_TRACE_THIS( ParametricOpening( in, mask, out, &filterSize, connectivity, polarity, ParametricOpeningMode::AREA_OPENING ));
}

void VolumeOpening(
      Image const& in,
      Image const& mask,
      Image& out,
      dfloat filterSize,
      dip::uint connectivity,
      String const& polarity
) {
   DIP_THROW_IF( filterSize <= 0, E::INVALID_PARAMETER );
   DIP_STACK_TRACE_THIS( ParametricOpening( in, mask, out, &filterSize, connectivity, polarity, ParametricOpeningMode::VOLUME_OPENING ));
}

} // namespace dip
<commit_msg>Fixed one more detail in AreaOpening.<commit_after>/*
 * DIPlib 3.0
 * This file contains the area opening and related functions.
 *
 * (c)2008, 2017, Cris Luengo.
 *
 * 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 "diplib.h"
#include "diplib/morphology.h"
#include "diplib/binary.h"
#include "diplib/neighborlist.h"
#include "diplib/iterators.h"
#include "diplib/boundary.h"
#include "diplib/overload.h"
#include "diplib/union_find.h"
#include "watershed_support.h"

namespace dip {

namespace {

/*
The code below is very similar to that for FastWatershed, but instead of recording the value of the local
minimum/maximum in each watershed basin, we record the grey value at which the size criterion was met.
This value can then be used to paint all pixels that are larger/smaller within the basin.
*/

template< typename TPI >
struct AreaOpenRegion {
   dip::uint size = 0;
   TPI lowest = 0;

   AreaOpenRegion() = default;
   explicit AreaOpenRegion( TPI value ) : size( 1 ), lowest( value ) {}

   dip::uint Param() const {
      return size;
   }

   bool ShouldMerge( AreaOpenRegion const& other, dip::uint filterSize ) const {
      return ( size + other.size - 1 ) < filterSize;
   }

   void Saturate( dip::uint filterSize ) {
      size = filterSize;
   }

   void AddRegionSize( AreaOpenRegion const& other ) {
      size += other.size - 1; // Any time we use this operator, the current pixel has already been added to both regions
   }

   void AddPixel( TPI value, dip::uint filterSize ) {
      if( size < filterSize ) {
         ++size;
         lowest = value;
      }
   }
};

/*
For the volume opening, we do the same thing but track the volume of the peak instead of its area.
*/

template< typename TPI >
struct VolumeOpenRegion {
   dip::uint size = 0;
   dfloat volume = 0;
   TPI lowest = 0;

   VolumeOpenRegion() = default;
   explicit VolumeOpenRegion( TPI value ) : size( 1 ), lowest( value ) {}

   dfloat Param() const {
      return volume;
   }

   bool ShouldMerge( VolumeOpenRegion const& other, dfloat filterSize ) const {
      return ( volume + other.volume ) < filterSize;
   }

   void Saturate( dfloat filterSize ) {
      volume = filterSize;
   }

   void AddRegionSize( VolumeOpenRegion const& other ) {
      size += other.size - 1; // Any time we use this operator, the current pixel has already been added to both regions, so there's no need to update `lowest`.
      volume += other.volume;
   }

   void AddPixel( TPI value, dfloat filterSize ) {
      if( volume < filterSize ) { // let's compute only if necessary
         dfloat height = std::abs( static_cast< dfloat >( lowest ) - static_cast< dfloat >( value ));
         if(( volume + static_cast< dfloat >( size ) * height ) < filterSize ) {
            // Adding the full height will not make this region larger than the filter size
            volume += static_cast< dfloat >( size ) * height;
            lowest = value;
         } else {
            // We need to add a smaller height to get the exact filter size.
            // But actually, the height is a little less than the exact result, so that our volume remains strictly less than `filterSize`.
            height = ( filterSize - volume ) / static_cast< dfloat >( size ) * ( 1 - 1e-6 );
            if( lowest > value ) {
               height = -height; // we want to subtract from `lowest`, rather than add, to move towards `value`.
            }
            volume = filterSize; // equal to `volume += static_cast< dfloat >( size ) * height` but without rounding errors.
            lowest = static_cast< TPI >( lowest + static_cast< TPI >( height )); // casting to integer will always round towards zero, which is just what we want to do here.
         }
         ++size;
      }
   }
};

template< typename TPI, typename RegionType >
RegionType AddRegions( RegionType region1, RegionType const& region2 ) {
   // When we get here, we've already added the current pixel to both regions, so the `lowest` value should be the same.
   DIP_ASSERT( region1.lowest == region2.lowest );
   region1.AddRegionSize( region2 );
   return region1;
}

template< typename UnionFunction, typename RegionType >
using ParametricOpenRegionList = UnionFind< LabelType, RegionType, UnionFunction >;

template< typename TPI, typename RegionType, typename ParamType >
void ParametricOpeningInternal(
      Image& c_grey,
      Image& c_labels,
      std::vector< dip::sint > const& offsets,
      IntegerArray const& neighborOffsets,
      ParamType filterSize,
      bool lowFirst
) {
   TPI* grey = static_cast< TPI* >( c_grey.Origin() );
   LabelType* labels = static_cast< LabelType* >( c_labels.Origin() );

   auto addRegionsFnc = AddRegions< TPI, RegionType >;
   ParametricOpenRegionList< decltype( addRegionsFnc ), RegionType > regions( addRegionsFnc );
   NeighborLabels neighborLabels;

   // Process first pixel
   labels[ offsets[ 0 ]] = regions.Create( RegionType( grey[ offsets[ 0 ]] ));

   // Process other pixels
   for( dip::uint ii = 1; ii < offsets.size(); ++ii ) {
      dip::sint offset = offsets[ ii ];
      if( lowFirst ? PixelIsInfinity( grey[ offset ] ) : PixelIsMinusInfinity( grey[ offset ] )) {
         break; // we're done
      }
      neighborLabels.Reset();
      for( auto o : neighborOffsets ) {
         neighborLabels.Push( regions.FindRoot( labels[ offset + o ] ));
      }
      if( neighborLabels.Size() == 0 ) {
         // Not touching a label: new label
         labels[ offset ] = regions.Create( RegionType( grey[ offset ] ));
      } else if( neighborLabels.Size() == 1 ) {
         // Touching a single label: grow
         LabelType lab = neighborLabels.Label( 0 );
         labels[ offset ] = lab;
         regions.Value( lab ).AddPixel( grey[ offset ], filterSize );
      } else {
         // Touching two or more labels
         // Grow each of the small regions, and find the label of the smallest region
         LabelType lab = neighborLabels.Label( 0 );
         ParamType size = regions.Value( lab ).Param();
         for( auto lab2 : neighborLabels ) {
            if( regions.Value( lab2 ).Param() < size ) {
               lab = lab2;
               size = regions.Value( lab ).Param();
            }
            regions.Value( lab2 ).AddPixel( grey[ offset ], filterSize );
         }
         // Assign the pixel to the smallest region
         labels[ offset ] = lab;
         if( regions.Value( lab ).Param() < filterSize ) {
            // If the region is still small, combine information from the other regions
            for( LabelType lab2 : neighborLabels ) {
               if( lab != lab2 ) {
                  if( regions.Value( lab ).ShouldMerge( regions.Value( lab2 ), filterSize )) {
                     regions.Union( lab, lab2 );
                  } else {
                     // If we don't merge, both regions should stop growing
                     regions.Value( lab ).Saturate( filterSize );
                     regions.Value( lab2 ).Saturate( filterSize );
                  }
               }
            }
         }
      }

   }
   JointImageIterator< TPI, LabelType > it( { c_grey, c_labels } );
   it.OptimizeAndFlatten();
   if( lowFirst ) {
      do {
         LabelType lab = it.template Sample< 1 >();
         if( lab > 0 ) {
            TPI v = regions.Value( lab ).lowest;
            if( it.template Sample< 0 >() < v ) {
               it.template Sample< 0 >() = v;
            }
         }
      } while( ++it );
   } else {
      do {
         LabelType lab = it.template Sample< 1 >();
         if( lab > 0 ) {
            TPI v = regions.Value( lab ).lowest;
            if( it.template Sample< 0 >() > v ) {
               it.template Sample< 0 >() = v;
            }
         }
      } while( ++it );
   }
}

template< typename TPI >
void AreaOpeningInternal(
      Image& grey,
      Image& labels,
      std::vector< dip::sint > const& offsets,
      IntegerArray const& neighborOffsets,
      dip::uint filterSize,
      bool lowFirst
) {
   ParametricOpeningInternal< TPI, AreaOpenRegion< TPI >>( grey, labels, offsets, neighborOffsets, filterSize, lowFirst );
}

template< typename TPI >
void VolumeOpeningInternal(
      Image& grey,
      Image& labels,
      std::vector< dip::sint > const& offsets,
      IntegerArray const& neighborOffsets,
      dfloat filterSize,
      bool lowFirst
) {
   ParametricOpeningInternal< TPI, VolumeOpenRegion< TPI >>( grey, labels, offsets, neighborOffsets, filterSize, lowFirst );
}

enum class ParametricOpeningMode {
      AREA_OPENING,
      VOLUME_OPENING
};

void ParametricOpening(
      Image const& c_in,
      Image const& c_mask,
      Image& out,
      void* filterSizePtr, // Points to either a dip::uint or a dip::dfloat, depending on `mode`. Sorry, don't know how else to do this nicely without a `constexpr if`. TODO: Upgrade to C++17?
      dip::uint connectivity,
      String const& polarity,
      ParametricOpeningMode mode
) {
   // Check input
   DIP_THROW_IF( !c_in.IsForged(), E::IMAGE_NOT_FORGED );
   DIP_THROW_IF( !c_in.IsScalar(), E::IMAGE_NOT_SCALAR );
   DIP_THROW_IF( !c_in.DataType().IsReal(), E::DATA_TYPE_NOT_SUPPORTED );
   dip::uint nDims = c_in.Dimensionality();
   DIP_THROW_IF( nDims < 1, E::DIMENSIONALITY_NOT_SUPPORTED );
   DIP_THROW_IF( connectivity > nDims, E::ILLEGAL_CONNECTIVITY );
   bool lowFirst;
   DIP_STACK_TRACE_THIS( lowFirst = BooleanFromString( polarity, S::CLOSING, S::OPENING ));

   // Add a 1-pixel boundary around the input image
   Image grey;
   ExtendImage( c_in, grey, { 1 }, { lowFirst ? BoundaryCondition::ADD_MAX_VALUE : BoundaryCondition::ADD_MIN_VALUE } );

   // Prepare labels image
   Image labels;
   labels.SetStrides( grey.Strides() );
   labels.SetSizes( grey.Sizes() );
   labels.SetDataType( DT_LABEL );
   labels.Forge();
   DIP_ASSERT( labels.Strides() == grey.Strides() );
   labels.Fill( 0 );

   // Check mask, expand mask singleton dimensions if necessary
   Image mask;
   bool hasMask = false;
   if( c_mask.IsForged() ) {
      mask = c_mask.QuickCopy();
      UnsignedArray const& inSizes = c_in.Sizes();
      DIP_START_STACK_TRACE
         mask.CheckIsMask( inSizes, Option::AllowSingletonExpansion::DO_ALLOW, Option::ThrowException::DO_THROW );
         mask.ExpandSingletonDimensions( inSizes );
      DIP_END_STACK_TRACE
      hasMask = true;
   }

   // Create sorted offsets array (skipping border)
   std::vector< dip::sint > offsets;
   if( hasMask ) {
      offsets = CreateOffsetsArray( mask, grey.Strides() );
      // The mask is shifted by one pixel, because we added a pixel to `grey`. Add the offset to the offsets!
      UnsignedArray pos( nDims, 1 );
      dip::sint firstPixelOffset = grey.Offset( pos );
      for( auto& o : offsets ) {
         o += firstPixelOffset;
      }
   } else {
      offsets = CreateOffsetsArray( grey.Sizes(), grey.Strides() );
   }
   if( offsets.empty() ) {
      // This can happen if `mask` is empty.
      return;
   }
   SortOffsets( grey, offsets, lowFirst );

   // Create array with offsets to neighbors
   NeighborList neighbors( { Metric::TypeCode::CONNECTED, connectivity }, nDims );
   IntegerArray neighborOffsets = neighbors.ComputeOffsets( grey.Strides() );

   // Do the data-type-dependent thing
   DIP_START_STACK_TRACE
      if( mode == ParametricOpeningMode::AREA_OPENING ) {
         DIP_OVL_CALL_REAL( AreaOpeningInternal, ( grey, labels, offsets, neighborOffsets, *static_cast< dip::uint* >( filterSizePtr ), lowFirst ), grey.DataType());
      } else {
         DIP_OVL_CALL_REAL( VolumeOpeningInternal, ( grey, labels, offsets, neighborOffsets, *static_cast< dfloat* >( filterSizePtr ), lowFirst ), grey.DataType());
      }
   DIP_END_STACK_TRACE

   // Copy result to output
   grey.Crop( c_in.Sizes() );
   PixelSize pixelSize = c_in.PixelSize();
   out.Copy( grey );
   out.SetPixelSize( std::move( pixelSize ));
}

} // namespace

void AreaOpening(
      Image const& in,
      Image const& mask,
      Image& out,
      dip::uint filterSize,
      dip::uint connectivity,
      String const& polarity
) {
   bool lowFirst;
   DIP_STACK_TRACE_THIS( lowFirst = BooleanFromString( polarity, S::CLOSING, S::OPENING ));
   if( in.DataType().IsBinary() ) {
      DIP_START_STACK_TRACE
         if( lowFirst ) {
            BinaryAreaClosing( in, out, filterSize, connectivity );
         } else {
            BinaryAreaOpening( in, out, filterSize, connectivity );
         }
      DIP_END_STACK_TRACE
      return;
   }
   DIP_STACK_TRACE_THIS( ParametricOpening( in, mask, out, &filterSize, connectivity, polarity, ParametricOpeningMode::AREA_OPENING ));
}

void VolumeOpening(
      Image const& in,
      Image const& mask,
      Image& out,
      dfloat filterSize,
      dip::uint connectivity,
      String const& polarity
) {
   DIP_THROW_IF( filterSize <= 0, E::INVALID_PARAMETER );
   DIP_STACK_TRACE_THIS( ParametricOpening( in, mask, out, &filterSize, connectivity, polarity, ParametricOpeningMode::VOLUME_OPENING ));
}

} // namespace dip
<|endoftext|>


<commit_before>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * 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 Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#include <algorithm> // sort
#include <cmath> //abs, fabs
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <vector>

#include <assert.h>
#include <map>
#include <nupic/math/StlIo.hpp>     // binary_save
#include <nupic/math/Math.hpp> // Epsilon

#include <nupic/utils/Log.hpp>
#include <nupic/utils/Random.hpp>

#include <nupic/algorithms/Segment.hpp>

using namespace nupic::algorithms::Cells4;

//----------------------------------------------------------------------
/**
 * Utility routine. Given a src cell index, prints synapse as:
 *  [column, cell within col]
 */
void printSynapse(UInt srcCellIdx, UInt nCellsPerCol) {
  UInt col = (UInt)(srcCellIdx / nCellsPerCol);
  UInt cell = srcCellIdx - col * nCellsPerCol;
  std::cout << "[" << col << "," << cell << "]  ";
}

//----------------------------------------------------------------------
Segment::Segment(InSynapses _s, Real frequency, bool seqSegFlag,
                 Real permConnected, UInt iteration)
    : _totalActivations(1), _positiveActivations(1), _lastActiveIteration(0),
      _lastPosDutyCycle(1.0 / iteration), _lastPosDutyCycleIteration(iteration),
      _seqSegFlag(seqSegFlag), _frequency(frequency), _synapses(std::move(_s)),
      _nConnected(0) {
  for (UInt i = 0; i != _synapses.size(); ++i)
    if (_synapses[i].permanence() >= permConnected)
      ++_nConnected;

  std::sort(_synapses.begin(), _synapses.end(), InSynapseOrder());
  NTA_ASSERT(invariants());
}

//--------------------------------------------------------------------------------
Segment &Segment::operator=(const Segment &o) {
  if (&o != this) {
    _seqSegFlag = o._seqSegFlag;
    _frequency = o._frequency;
    _synapses = o._synapses;
    _nConnected = o._nConnected;
    _totalActivations = o._totalActivations;
    _positiveActivations = o._positiveActivations;
    _lastActiveIteration = o._lastActiveIteration;
    _lastPosDutyCycle = o._lastPosDutyCycle;
    _lastPosDutyCycleIteration = o._lastPosDutyCycleIteration;
  }
  NTA_ASSERT(invariants());
  return *this;
}

//--------------------------------------------------------------------------------
bool Segment::equals(const Segment &other) const {
  if (_totalActivations != other._totalActivations ||
      _positiveActivations != other._positiveActivations ||
      _lastActiveIteration != other._lastActiveIteration ||
      abs(_lastPosDutyCycle - other._lastPosDutyCycle) > nupic::Epsilon ||
      _lastPosDutyCycleIteration  !=  other._lastPosDutyCycleIteration ||
      _seqSegFlag != other._seqSegFlag ||
      abs(_frequency - other._frequency) > nupic::Epsilon||
      _nConnected != other._nConnected) {
    return false;
  }
  return _synapses == other._synapses;
}

//--------------------------------------------------------------------------------
Segment::Segment(const Segment &o)
    : _totalActivations(o._totalActivations),
      _positiveActivations(o._positiveActivations),
      _lastActiveIteration(o._lastActiveIteration),
      _lastPosDutyCycle(o._lastPosDutyCycle),
      _lastPosDutyCycleIteration(o._lastPosDutyCycleIteration),
      _seqSegFlag(o._seqSegFlag), _frequency(o._frequency),
      _synapses(o._synapses), _nConnected(o._nConnected) {
  NTA_ASSERT(invariants());
}

bool Segment::isActive(const CState &activities, Real permConnected,
                       UInt activationThreshold) const {
  { NTA_ASSERT(invariants()); }

  UInt activity = 0;

  if (_nConnected < activationThreshold)
    return false;

  // TODO: maintain nPermConnected incrementally??
  for (UInt i = 0; i != size() && activity < activationThreshold; ++i)
    if (_synapses[i].permanence() >= permConnected &&
        activities.isSet(_synapses[i].srcCellIdx()))
      activity++;

  return activity >= activationThreshold;
}

//----------------------------------------------------------------------
/**
 * Compute/update and return the positive activations duty cycle of
 * this segment. This is a measure of how often this segment is
 * providing good predictions.
 *
 */
Real Segment::dutyCycle(UInt iteration, bool active, bool readOnly) {
  { NTA_ASSERT(iteration > 0); }

  Real dutyCycle = 0.0;

  // For tier 0, compute it from total number of positive activations seen
  if (iteration <= _dutyCycleTiers[1]) {
    dutyCycle = ((Real)_positiveActivations) / iteration;
    if (!readOnly) {
      _lastPosDutyCycleIteration = iteration;
      _lastPosDutyCycle = dutyCycle;
    }
    return dutyCycle;
  }

  // How old is our update?
  UInt age = iteration - _lastPosDutyCycleIteration;

  // If it's already up to date we can return our cached value
  if (age == 0 && !active)
    return _lastPosDutyCycle;

  // Figure out which alpha we're using
  Real alpha = 0;
  for (UInt tierIdx = _numTiers - 1; tierIdx > 0; tierIdx--) {
    if (iteration > _dutyCycleTiers[tierIdx]) {
      alpha = _dutyCycleAlphas[tierIdx];
      break;
    }
  }

  // Update duty cycle
  dutyCycle = (Real)pow((Real64)(1.0 - alpha), (Real64)age) * _lastPosDutyCycle;
  if (active)
    dutyCycle += alpha;

  // Update the time we computed it
  if (!readOnly) {
    _lastPosDutyCycle = dutyCycle;
    _lastPosDutyCycleIteration = iteration;
  }

  return dutyCycle;
}

UInt Segment::computeActivity(const CState &activities, Real permConnected,
                              bool connectedSynapsesOnly) const

{
  { NTA_ASSERT(invariants()); }

  UInt activity = 0;

  if (connectedSynapsesOnly) {
    for (UInt i = 0; i != size(); ++i)
      if (activities.isSet(_synapses[i].srcCellIdx()) &&
          (_synapses[i].permanence() >= permConnected))
        activity++;
  } else {
    for (UInt i = 0; i != size(); ++i)
      if (activities.isSet(_synapses[i].srcCellIdx()))
        activity++;
  }

  return activity;
}

void Segment::addSynapses(const std::set<UInt> &srcCells, Real initStrength,
                          Real permConnected) {
  auto srcCellIdx = srcCells.begin();

  for (; srcCellIdx != srcCells.end(); ++srcCellIdx) {
    _synapses.push_back(InSynapse(*srcCellIdx, initStrength));
    if (initStrength >= permConnected)
      ++_nConnected;
  }

  std::sort(_synapses.begin(), _synapses.end(), InSynapseOrder());
  NTA_ASSERT(invariants()); // will catch non-unique synapses
}

void Segment::decaySynapses(Real decay, std::vector<UInt> &removed,
                            Real permConnected, bool doDecay) {
  NTA_ASSERT(invariants());

  if (_synapses.empty())
    return;

  static std::vector<UInt> del;
  del.clear(); // purge residual data

  for (UInt i = 0; i != _synapses.size(); ++i) {

    int wasConnected = (int)(_synapses[i].permanence() >= permConnected);

    if (_synapses[i].permanence() < decay) {

      removed.push_back(_synapses[i].srcCellIdx());
      del.push_back(i);

    } else if (doDecay) {
      _synapses[i].permanence() -= decay;
    }

    int isConnected = (int)(_synapses[i].permanence() >= permConnected);

    _nConnected += isConnected - wasConnected;
  }

  _removeSynapses(del);

  NTA_ASSERT(invariants());
}

//--------------------------------------------------------------------------------
/**
 * Subtract decay from each synapses' permanence value.
 * Synapses whose permanence drops below 0 are removed and their indices
 * are inserted into the "removed" list.
 *
 */
void Segment::decaySynapses2(Real decay, std::vector<UInt> &removed,
                             Real permConnected) {
  NTA_ASSERT(invariants());

  if (_synapses.empty())
    return;

  static std::vector<UInt> del;
  del.clear(); // purge residual data

  for (UInt i = 0; i != _synapses.size(); ++i) {

    // Remove synapse whose permanence will go to zero or below.
    if (_synapses[i].permanence() <= decay) {

      // If it was connected, reduce our connected count
      if (_synapses[i].permanence() >= permConnected)
        _nConnected--;

      // Add this synapse to list of synapses to be removed
      removed.push_back(_synapses[i].srcCellIdx());
      del.push_back(i);

    } else {

      _synapses[i].permanence() -= decay;

      // If it was connected and is now below permanence, reduce connected count
      if ((_synapses[i].permanence() + decay >= permConnected) &&
          (_synapses[i].permanence() < permConnected))
        _nConnected--;
    }
  }

  _removeSynapses(del);

  NTA_ASSERT(invariants());
}

//-----------------------------------------------------------------------
/**
 * Sort order for InSynapse's. Cells are sorted in order of increasing
 * permanence.
 *
 */
struct InPermanenceOrder {
  inline bool operator()(const InSynapse &a, const InSynapse &b) const {
    return a.permanence() < b.permanence();
  }
};

//-----------------------------------------------------------------------
/**
 * Sort order for list of source cell indices. Cells are sorted in order of
 * increasing source cell index.
 *
 */
struct InSrcCellOrder {
  inline bool operator()(const UInt a, const UInt b) const { return a < b; }
};

//----------------------------------------------------------------------
/**
 * Free up some synapses in this segment. We always free up inactive
 * synapses (lowest permanence freed up first) before we start to free
 * up active ones.
 */
void Segment::freeNSynapses(UInt numToFree,
                            std::vector<UInt> &inactiveSynapseIndices,
                            std::vector<UInt> &inactiveSegmentIndices,
                            std::vector<UInt> &activeSynapseIndices,
                            std::vector<UInt> &activeSegmentIndices,
                            std::vector<UInt> &removed, UInt verbosity,
                            UInt nCellsPerCol, Real permMax) {
  NTA_ASSERT(inactiveSegmentIndices.size() == inactiveSynapseIndices.size());
  NTA_ASSERT(activeSegmentIndices.size() == activeSynapseIndices.size());
  NTA_ASSERT(numToFree <= _synapses.size());
  NTA_ASSERT(numToFree <=
             (inactiveSegmentIndices.size() + activeSegmentIndices.size()));

  if (verbosity >= 4) {
    std::cout << "\nIn CPP freeNSynapses with numToFree = " << numToFree
              << ", inactiveSynapses = ";
    for (auto &inactiveSynapseIndice : inactiveSynapseIndices) {
      printSynapse(inactiveSynapseIndice, nCellsPerCol);
    }
    std::cout << "\n";
  }

  //----------------------------------------------------------------------
  // Collect candidate synapses for deletion

  // We first choose from inactive synapses, in order of increasing permanence
  InSynapses candidates;
  for (UInt i = 0; i < inactiveSegmentIndices.size(); i++) {
    // Put in *segment indices*, not source cell indices
    candidates.push_back(
        InSynapse(inactiveSegmentIndices[i],
                  _synapses[inactiveSegmentIndices[i]].permanence()));
  }

  // If we need more, choose from active synapses in order of increasing
  // permanence values. This set has lower priority than inactive synapses
  // so we add a constant permanence value for sorting purposes
  if (candidates.size() < numToFree) {
    for (UInt i = 0; i < activeSegmentIndices.size(); i++) {
      // Put in *segment indices*, not source cell indices
      candidates.push_back(
          InSynapse(activeSegmentIndices[i],
                    _synapses[activeSegmentIndices[i]].permanence() + permMax));
    }
  }

  // Now sort the list of candidate synapses
  std::stable_sort(candidates.begin(), candidates.end(), InPermanenceOrder());

  //----------------------------------------------------------------------
  // Create the final list of synapses we will remove
  static std::vector<UInt> del;
  del.clear(); // purge residual data
  for (UInt i = 0; i < numToFree; i++) {
    del.push_back(candidates[i].srcCellIdx());
    UInt cellIdx = _synapses[candidates[i].srcCellIdx()].srcCellIdx();
    removed.push_back(cellIdx);
  }

  // Debug statements
  if (verbosity >= 4) {
    std::cout << "Removing these synapses: ";
    for (auto &elem : removed) {
      printSynapse(elem, nCellsPerCol);
    }
    std::cout << "\n";

    std::cout << "Segment BEFORE remove synapses: ";
    print(std::cout, nCellsPerCol);
    std::cout << "\n";
  }

  //----------------------------------------------------------------------
  // Remove the synapses
  if (numToFree > 0) {
    std::sort(del.begin(), del.end(), InSrcCellOrder());
    _removeSynapses(del);
  }

  // Debug statements
  if (verbosity >= 4) {
    std::cout << "Segment AFTER remove synapses: ";
    print(std::cout, nCellsPerCol);
    std::cout << "\n";
  }
}

void Segment::print(std::ostream &outStream, UInt nCellsPerCol) const {
  outStream << (_seqSegFlag ? "True " : "False ") << "dc"
            << std::setprecision(4) << _lastPosDutyCycle << " ("
            << _positiveActivations << "/" << _totalActivations << ") ";
  for (UInt i = 0; i != _synapses.size(); ++i) {
    if (nCellsPerCol > 0) {
      UInt cellIdx = _synapses[i].srcCellIdx();
      UInt col = (UInt)(cellIdx / nCellsPerCol);
      UInt cell = cellIdx - col * nCellsPerCol;
      outStream << "[" << col << "," << cell << "]" << std::setprecision(4)
                << _synapses[i].permanence() << " ";
    } else {
      outStream << _synapses[i];
    }
    if (i < _synapses.size() -1)
      outStream << " ";
  }
  outStream << std::endl;
}

namespace nupic{
  namespace algorithms {
    namespace Cells4 {

std::ostream &operator<<(std::ostream &outStream, const Segment &seg) {
  seg.print(outStream);
  return outStream;
}

std::ostream &operator<<(std::ostream &outStream, const CState &cstate) {
  cstate.print(outStream);
  return outStream;
}

std::ostream &operator<<(std::ostream &outStream, const CStateIndexed &cstate) {
  cstate.print(outStream);
  return outStream;
}
} // namespace Cells4
} // namespace algorithms
} // namespace nupic
<commit_msg>Segment: fix for clang<commit_after>/* ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013, Numenta, Inc.  Unless you have an agreement
 * with Numenta, Inc., for a separate license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * 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 Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

#include <algorithm> // sort
#include <cmath> //abs, fabs
#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>
#include <vector>

#include <assert.h>
#include <map>
#include <nupic/math/StlIo.hpp>     // binary_save
#include <nupic/math/Math.hpp> // Epsilon

#include <nupic/utils/Log.hpp>
#include <nupic/utils/Random.hpp>

#include <nupic/algorithms/Segment.hpp>

using namespace nupic::algorithms::Cells4;

//----------------------------------------------------------------------
/**
 * Utility routine. Given a src cell index, prints synapse as:
 *  [column, cell within col]
 */
void printSynapse(UInt srcCellIdx, UInt nCellsPerCol) {
  UInt col = (UInt)(srcCellIdx / nCellsPerCol);
  UInt cell = srcCellIdx - col * nCellsPerCol;
  std::cout << "[" << col << "," << cell << "]  ";
}

//----------------------------------------------------------------------
Segment::Segment(InSynapses _s, Real frequency, bool seqSegFlag,
                 Real permConnected, UInt iteration)
    : _totalActivations(1), _positiveActivations(1), _lastActiveIteration(0),
      _lastPosDutyCycle(1.0 / iteration), _lastPosDutyCycleIteration(iteration),
      _seqSegFlag(seqSegFlag), _frequency(frequency), _synapses(std::move(_s)),
      _nConnected(0) {
  for (UInt i = 0; i != _synapses.size(); ++i)
    if (_synapses[i].permanence() >= permConnected)
      ++_nConnected;

  std::sort(_synapses.begin(), _synapses.end(), InSynapseOrder());
  NTA_ASSERT(invariants());
}

//--------------------------------------------------------------------------------
Segment &Segment::operator=(const Segment &o) {
  if (&o != this) {
    _seqSegFlag = o._seqSegFlag;
    _frequency = o._frequency;
    _synapses = o._synapses;
    _nConnected = o._nConnected;
    _totalActivations = o._totalActivations;
    _positiveActivations = o._positiveActivations;
    _lastActiveIteration = o._lastActiveIteration;
    _lastPosDutyCycle = o._lastPosDutyCycle;
    _lastPosDutyCycleIteration = o._lastPosDutyCycleIteration;
  }
  NTA_ASSERT(invariants());
  return *this;
}

//--------------------------------------------------------------------------------
bool Segment::equals(const Segment &other) const {
  if (_totalActivations != other._totalActivations ||
      _positiveActivations != other._positiveActivations ||
      _lastActiveIteration != other._lastActiveIteration ||
      std::abs(_lastPosDutyCycle - other._lastPosDutyCycle) > nupic::Epsilon ||
      _lastPosDutyCycleIteration  !=  other._lastPosDutyCycleIteration ||
      _seqSegFlag != other._seqSegFlag ||
      std::abs(_frequency - other._frequency) > nupic::Epsilon||
      _nConnected != other._nConnected) {
    return false;
  }
  return _synapses == other._synapses;
}

//--------------------------------------------------------------------------------
Segment::Segment(const Segment &o)
    : _totalActivations(o._totalActivations),
      _positiveActivations(o._positiveActivations),
      _lastActiveIteration(o._lastActiveIteration),
      _lastPosDutyCycle(o._lastPosDutyCycle),
      _lastPosDutyCycleIteration(o._lastPosDutyCycleIteration),
      _seqSegFlag(o._seqSegFlag), _frequency(o._frequency),
      _synapses(o._synapses), _nConnected(o._nConnected) {
  NTA_ASSERT(invariants());
}

bool Segment::isActive(const CState &activities, Real permConnected,
                       UInt activationThreshold) const {
  { NTA_ASSERT(invariants()); }

  UInt activity = 0;

  if (_nConnected < activationThreshold)
    return false;

  // TODO: maintain nPermConnected incrementally??
  for (UInt i = 0; i != size() && activity < activationThreshold; ++i)
    if (_synapses[i].permanence() >= permConnected &&
        activities.isSet(_synapses[i].srcCellIdx()))
      activity++;

  return activity >= activationThreshold;
}

//----------------------------------------------------------------------
/**
 * Compute/update and return the positive activations duty cycle of
 * this segment. This is a measure of how often this segment is
 * providing good predictions.
 *
 */
Real Segment::dutyCycle(UInt iteration, bool active, bool readOnly) {
  { NTA_ASSERT(iteration > 0); }

  Real dutyCycle = 0.0;

  // For tier 0, compute it from total number of positive activations seen
  if (iteration <= _dutyCycleTiers[1]) {
    dutyCycle = ((Real)_positiveActivations) / iteration;
    if (!readOnly) {
      _lastPosDutyCycleIteration = iteration;
      _lastPosDutyCycle = dutyCycle;
    }
    return dutyCycle;
  }

  // How old is our update?
  UInt age = iteration - _lastPosDutyCycleIteration;

  // If it's already up to date we can return our cached value
  if (age == 0 && !active)
    return _lastPosDutyCycle;

  // Figure out which alpha we're using
  Real alpha = 0;
  for (UInt tierIdx = _numTiers - 1; tierIdx > 0; tierIdx--) {
    if (iteration > _dutyCycleTiers[tierIdx]) {
      alpha = _dutyCycleAlphas[tierIdx];
      break;
    }
  }

  // Update duty cycle
  dutyCycle = (Real)pow((Real64)(1.0 - alpha), (Real64)age) * _lastPosDutyCycle;
  if (active)
    dutyCycle += alpha;

  // Update the time we computed it
  if (!readOnly) {
    _lastPosDutyCycle = dutyCycle;
    _lastPosDutyCycleIteration = iteration;
  }

  return dutyCycle;
}

UInt Segment::computeActivity(const CState &activities, Real permConnected,
                              bool connectedSynapsesOnly) const

{
  { NTA_ASSERT(invariants()); }

  UInt activity = 0;

  if (connectedSynapsesOnly) {
    for (UInt i = 0; i != size(); ++i)
      if (activities.isSet(_synapses[i].srcCellIdx()) &&
          (_synapses[i].permanence() >= permConnected))
        activity++;
  } else {
    for (UInt i = 0; i != size(); ++i)
      if (activities.isSet(_synapses[i].srcCellIdx()))
        activity++;
  }

  return activity;
}

void Segment::addSynapses(const std::set<UInt> &srcCells, Real initStrength,
                          Real permConnected) {
  auto srcCellIdx = srcCells.begin();

  for (; srcCellIdx != srcCells.end(); ++srcCellIdx) {
    _synapses.push_back(InSynapse(*srcCellIdx, initStrength));
    if (initStrength >= permConnected)
      ++_nConnected;
  }

  std::sort(_synapses.begin(), _synapses.end(), InSynapseOrder());
  NTA_ASSERT(invariants()); // will catch non-unique synapses
}

void Segment::decaySynapses(Real decay, std::vector<UInt> &removed,
                            Real permConnected, bool doDecay) {
  NTA_ASSERT(invariants());

  if (_synapses.empty())
    return;

  static std::vector<UInt> del;
  del.clear(); // purge residual data

  for (UInt i = 0; i != _synapses.size(); ++i) {

    int wasConnected = (int)(_synapses[i].permanence() >= permConnected);

    if (_synapses[i].permanence() < decay) {

      removed.push_back(_synapses[i].srcCellIdx());
      del.push_back(i);

    } else if (doDecay) {
      _synapses[i].permanence() -= decay;
    }

    int isConnected = (int)(_synapses[i].permanence() >= permConnected);

    _nConnected += isConnected - wasConnected;
  }

  _removeSynapses(del);

  NTA_ASSERT(invariants());
}

//--------------------------------------------------------------------------------
/**
 * Subtract decay from each synapses' permanence value.
 * Synapses whose permanence drops below 0 are removed and their indices
 * are inserted into the "removed" list.
 *
 */
void Segment::decaySynapses2(Real decay, std::vector<UInt> &removed,
                             Real permConnected) {
  NTA_ASSERT(invariants());

  if (_synapses.empty())
    return;

  static std::vector<UInt> del;
  del.clear(); // purge residual data

  for (UInt i = 0; i != _synapses.size(); ++i) {

    // Remove synapse whose permanence will go to zero or below.
    if (_synapses[i].permanence() <= decay) {

      // If it was connected, reduce our connected count
      if (_synapses[i].permanence() >= permConnected)
        _nConnected--;

      // Add this synapse to list of synapses to be removed
      removed.push_back(_synapses[i].srcCellIdx());
      del.push_back(i);

    } else {

      _synapses[i].permanence() -= decay;

      // If it was connected and is now below permanence, reduce connected count
      if ((_synapses[i].permanence() + decay >= permConnected) &&
          (_synapses[i].permanence() < permConnected))
        _nConnected--;
    }
  }

  _removeSynapses(del);

  NTA_ASSERT(invariants());
}

//-----------------------------------------------------------------------
/**
 * Sort order for InSynapse's. Cells are sorted in order of increasing
 * permanence.
 *
 */
struct InPermanenceOrder {
  inline bool operator()(const InSynapse &a, const InSynapse &b) const {
    return a.permanence() < b.permanence();
  }
};

//-----------------------------------------------------------------------
/**
 * Sort order for list of source cell indices. Cells are sorted in order of
 * increasing source cell index.
 *
 */
struct InSrcCellOrder {
  inline bool operator()(const UInt a, const UInt b) const { return a < b; }
};

//----------------------------------------------------------------------
/**
 * Free up some synapses in this segment. We always free up inactive
 * synapses (lowest permanence freed up first) before we start to free
 * up active ones.
 */
void Segment::freeNSynapses(UInt numToFree,
                            std::vector<UInt> &inactiveSynapseIndices,
                            std::vector<UInt> &inactiveSegmentIndices,
                            std::vector<UInt> &activeSynapseIndices,
                            std::vector<UInt> &activeSegmentIndices,
                            std::vector<UInt> &removed, UInt verbosity,
                            UInt nCellsPerCol, Real permMax) {
  NTA_ASSERT(inactiveSegmentIndices.size() == inactiveSynapseIndices.size());
  NTA_ASSERT(activeSegmentIndices.size() == activeSynapseIndices.size());
  NTA_ASSERT(numToFree <= _synapses.size());
  NTA_ASSERT(numToFree <=
             (inactiveSegmentIndices.size() + activeSegmentIndices.size()));

  if (verbosity >= 4) {
    std::cout << "\nIn CPP freeNSynapses with numToFree = " << numToFree
              << ", inactiveSynapses = ";
    for (auto &inactiveSynapseIndice : inactiveSynapseIndices) {
      printSynapse(inactiveSynapseIndice, nCellsPerCol);
    }
    std::cout << "\n";
  }

  //----------------------------------------------------------------------
  // Collect candidate synapses for deletion

  // We first choose from inactive synapses, in order of increasing permanence
  InSynapses candidates;
  for (UInt i = 0; i < inactiveSegmentIndices.size(); i++) {
    // Put in *segment indices*, not source cell indices
    candidates.push_back(
        InSynapse(inactiveSegmentIndices[i],
                  _synapses[inactiveSegmentIndices[i]].permanence()));
  }

  // If we need more, choose from active synapses in order of increasing
  // permanence values. This set has lower priority than inactive synapses
  // so we add a constant permanence value for sorting purposes
  if (candidates.size() < numToFree) {
    for (UInt i = 0; i < activeSegmentIndices.size(); i++) {
      // Put in *segment indices*, not source cell indices
      candidates.push_back(
          InSynapse(activeSegmentIndices[i],
                    _synapses[activeSegmentIndices[i]].permanence() + permMax));
    }
  }

  // Now sort the list of candidate synapses
  std::stable_sort(candidates.begin(), candidates.end(), InPermanenceOrder());

  //----------------------------------------------------------------------
  // Create the final list of synapses we will remove
  static std::vector<UInt> del;
  del.clear(); // purge residual data
  for (UInt i = 0; i < numToFree; i++) {
    del.push_back(candidates[i].srcCellIdx());
    UInt cellIdx = _synapses[candidates[i].srcCellIdx()].srcCellIdx();
    removed.push_back(cellIdx);
  }

  // Debug statements
  if (verbosity >= 4) {
    std::cout << "Removing these synapses: ";
    for (auto &elem : removed) {
      printSynapse(elem, nCellsPerCol);
    }
    std::cout << "\n";

    std::cout << "Segment BEFORE remove synapses: ";
    print(std::cout, nCellsPerCol);
    std::cout << "\n";
  }

  //----------------------------------------------------------------------
  // Remove the synapses
  if (numToFree > 0) {
    std::sort(del.begin(), del.end(), InSrcCellOrder());
    _removeSynapses(del);
  }

  // Debug statements
  if (verbosity >= 4) {
    std::cout << "Segment AFTER remove synapses: ";
    print(std::cout, nCellsPerCol);
    std::cout << "\n";
  }
}

void Segment::print(std::ostream &outStream, UInt nCellsPerCol) const {
  outStream << (_seqSegFlag ? "True " : "False ") << "dc"
            << std::setprecision(4) << _lastPosDutyCycle << " ("
            << _positiveActivations << "/" << _totalActivations << ") ";
  for (UInt i = 0; i != _synapses.size(); ++i) {
    if (nCellsPerCol > 0) {
      UInt cellIdx = _synapses[i].srcCellIdx();
      UInt col = (UInt)(cellIdx / nCellsPerCol);
      UInt cell = cellIdx - col * nCellsPerCol;
      outStream << "[" << col << "," << cell << "]" << std::setprecision(4)
                << _synapses[i].permanence() << " ";
    } else {
      outStream << _synapses[i];
    }
    if (i < _synapses.size() -1)
      outStream << " ";
  }
  outStream << std::endl;
}

namespace nupic{
  namespace algorithms {
    namespace Cells4 {

std::ostream &operator<<(std::ostream &outStream, const Segment &seg) {
  seg.print(outStream);
  return outStream;
}

std::ostream &operator<<(std::ostream &outStream, const CState &cstate) {
  cstate.print(outStream);
  return outStream;
}

std::ostream &operator<<(std::ostream &outStream, const CStateIndexed &cstate) {
  cstate.print(outStream);
  return outStream;
}
} // namespace Cells4
} // namespace algorithms
} // namespace nupic
<|endoftext|>