Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/analyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2922,6 +2922,16 @@ void Analyser::AnalyserImpl::analyseModel(const ModelPtr &model)
aie2aetMappings.emplace(internalEquation, type);
}

// Retrieve the equations used to compute each variable, in equation order.

std::unordered_map<AnalyserInternalVariable *, std::vector<AnalyserInternalEquationPtr>> equationsOfVariable;

for (const auto &internalEquation : mInternalEquations) {
for (const auto &unknownVariable : internalEquation->mUnknownVariables) {
equationsOfVariable[unknownVariable.get()].push_back(internalEquation);
}
}

// Make our internal variables available through our API.

std::map<AnalyserInternalVariablePtr, AnalyserVariablePtr> aiv2avMappings;
Expand Down Expand Up @@ -2970,9 +2980,10 @@ void Analyser::AnalyserImpl::analyseModel(const ModelPtr &model)

AnalyserEquationPtrs equations;
auto isNlaEquation = false;
auto computing = equationsOfVariable.find(internalVariable.get());

for (const auto &internalEquation : mInternalEquations) {
if (std::find(internalEquation->mUnknownVariables.begin(), internalEquation->mUnknownVariables.end(), internalVariable) != internalEquation->mUnknownVariables.end()) {
if (computing != equationsOfVariable.end()) {
for (const auto &internalEquation : computing->second) {
equations.push_back(aie2aeMappings[internalEquation]);

if ((aie2aetMappings.find(internalEquation) != aie2aetMappings.end())
Expand Down Expand Up @@ -3164,6 +3175,8 @@ void Analyser::AnalyserImpl::analyseModel(const ModelPtr &model)

analyserEquation->mPimpl->mIsStateRateBased = isStateRateBased(analyserEquation, checkedEquations);
}

mAnalyserModel->mPimpl->buildAnalyserVariableByGroup();
}

AnalyserExternalVariablePtrs::const_iterator Analyser::AnalyserImpl::findExternalVariable(const VariablePtr &variable) const
Expand Down
55 changes: 29 additions & 26 deletions src/analysermodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,47 +284,50 @@ AnalyserVariablePtr AnalyserModel::externalVariable(size_t index) const
return mPimpl->mExternalVariables[index];
}

AnalyserVariablePtr AnalyserModel::analyserVariable(const VariablePtr &variable)
void AnalyserModel::AnalyserModelImpl::buildAnalyserVariableByGroup()
{
if (!isValid() || (variable == nullptr)) {
return {};
auto add = [this](const AnalyserVariablePtr &analyserVariable) {
mAnalyserVariableByGroup.emplace(mEquivalentVariableCache.at(reinterpret_cast<uintptr_t>(analyserVariable->variable().get())), analyserVariable);
};

if (mVoi != nullptr) {
add(mVoi);
}

if (mPimpl->mVoi && areEquivalentVariables(variable, mPimpl->mVoi->variable())) {
return mPimpl->mVoi;
for (const auto &state : mStates) {
add(state);
}

for (const auto &state : mPimpl->mStates) {
if (areEquivalentVariables(variable, state->variable())) {
return state;
}
for (const auto &constant : mConstants) {
add(constant);
}

for (const auto &constant : mPimpl->mConstants) {
if (areEquivalentVariables(variable, constant->variable())) {
return constant;
}
for (const auto &computedConstant : mComputedConstants) {
add(computedConstant);
}

for (const auto &computedConstant : mPimpl->mComputedConstants) {
if (areEquivalentVariables(variable, computedConstant->variable())) {
return computedConstant;
}
for (const auto &algebraicVariable : mAlgebraicVariables) {
add(algebraicVariable);
}

for (const auto &algebraicVariable : mPimpl->mAlgebraicVariables) {
if (areEquivalentVariables(variable, algebraicVariable->variable())) {
return algebraicVariable;
}
for (const auto &externalVariable : mExternalVariables) {
add(externalVariable);
}
}

for (const auto &externalVariable : mPimpl->mExternalVariables) {
if (areEquivalentVariables(variable, externalVariable->variable())) {
return externalVariable;
}
AnalyserVariablePtr AnalyserModel::analyserVariable(const VariablePtr &variable)
{
if (!isValid() || (variable == nullptr)) {
return {};
}

auto group = mPimpl->mEquivalentVariableCache.find(reinterpret_cast<uintptr_t>(variable.get()));

if (group == mPimpl->mEquivalentVariableCache.end()) {
return {};
}

return {};
return mPimpl->mAnalyserVariableByGroup.at(group->second);
}

size_t AnalyserModel::analyserEquationCount() const
Expand Down
3 changes: 3 additions & 0 deletions src/analysermodel_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct AnalyserModel::AnalyserModelImpl
std::vector<AnalyserEquationPtr> mAnalyserEquations;

std::unordered_map<uintptr_t, size_t> mEquivalentVariableCache;
std::unordered_map<size_t, AnalyserVariablePtr> mAnalyserVariableByGroup;

bool mNeedEqFunction = false;
bool mNeedNeqFunction = false;
Expand Down Expand Up @@ -81,6 +82,8 @@ struct AnalyserModel::AnalyserModelImpl
void buildEquivalentVariablesCache(const ComponentPtr &component, std::unordered_set<uintptr_t> &visited, size_t &groupCount);
void buildEquivalentVariablesCache();

void buildAnalyserVariableByGroup();

AnalyserModelImpl(const ModelPtr &model);
};

Expand Down
20 changes: 12 additions & 8 deletions src/generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,39 @@ namespace libcellml {
void Generator::GeneratorImpl::reset()
{
mCode = {};
mTrackedIndexCache.clear();
}

std::string Generator::GeneratorImpl::analyserVariableIndexString(const AnalyserVariablePtr &analyserVariable)
{
// Determine the actual index of the analyser variable in the list of analyser variables by accounting for the fact
// that some analyser variables may be untracked.

auto cached = mTrackedIndexCache.find(analyserVariable.get());

if (cached != mTrackedIndexCache.end()) {
return convertToString(cached->second);
}

auto analyserVariables = libcellml::analyserVariables(analyserVariable);

if (analyserVariables.empty()) {
return convertToString(analyserVariable->index());
}

size_t i = MAX_SIZE_T;
size_t res = MAX_SIZE_T;
// Index every variable of this list: a tracked variable's index is the number of tracked variables before it.

for (;;) {
auto analyserVar = analyserVariables[++i];
size_t res = MAX_SIZE_T;

for (const auto &analyserVar : analyserVariables) {
if (isTrackedVariable(analyserVar, true)) {
++res;
}

if (analyserVariable == analyserVar) {
break;
}
mTrackedIndexCache[analyserVar.get()] = res;
}

return convertToString(res);
return convertToString(mTrackedIndexCache[analyserVariable.get()]);
}

bool Generator::GeneratorImpl::isTrackedEquation(const AnalyserEquationPtr &analyserEquation, bool tracked)
Expand Down
4 changes: 4 additions & 0 deletions src/generator_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ limitations under the License.

#pragma once

#include <unordered_map>

#include "libcellml/generator.h"

#include "libcellml/generatorprofile.h"
Expand All @@ -42,6 +44,8 @@ struct Generator::GeneratorImpl: public Logger::LoggerImpl
GeneratorProfilePtr mProfile;
GeneratorVariableTrackerPtr mVariableTracker;

std::unordered_map<const AnalyserVariable *, size_t> mTrackedIndexCache;

void reset();

std::string analyserVariableIndexString(const AnalyserVariablePtr &analyserVariable);
Expand Down
54 changes: 48 additions & 6 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.

#include <algorithm>
#include <string>
#include <unordered_map>
#include <vector>

#include "libcellml/component.h"
Expand Down Expand Up @@ -99,6 +100,27 @@ class Parser::ParserImpl: public Logger::LoggerImpl
*/
void loadConnection(const ModelPtr &model, const XmlNodePtr &node, NamePairList &usedConnections);

std::unordered_map<std::string, ComponentPtr> mComponentsByName; /**< The model's components by name, for loading its connections. */

/**
* @brief Add the components of @p entity and its descendants to mComponentsByName.
*
* A name keeps the first component found in the order Model::component(name, true) searches: an entity's own
* components, then the descendants of each in turn.
*
* @param entity The @c ComponentEntity whose components to add.
*/
void indexComponents(const ComponentEntityPtr &entity);

/**
* @brief The component named @p name in mComponentsByName, or @c nullptr.
*
* @param name The component's name.
*
* @return The component, or @c nullptr if there is none.
*/
ComponentPtr findComponentByName(const std::string &name) const;

/**
* @brief Update the @p model with an encapsulation parsed from @p node.
*
Expand Down Expand Up @@ -602,9 +624,14 @@ void Parser::ParserImpl::loadModel(const ModelPtr &model, const std::string &inp
}

NamePairList usedConnections;
mComponentsByName.clear();
if (!connectionNodes.empty()) {
indexComponents(model);
}
for (const auto &connectionNode : connectionNodes) {
loadConnection(model, connectionNode, usedConnections);
}
mComponentsByName.clear();

// Link units to their names.
DescriptionList issueList;
Expand Down Expand Up @@ -997,6 +1024,23 @@ void Parser::ParserImpl::loadVariable(const VariablePtr &variable, const XmlNode
}
}

void Parser::ParserImpl::indexComponents(const ComponentEntityPtr &entity)
{
for (size_t i = 0; i < entity->componentCount(); ++i) {
auto component = entity->component(i);
mComponentsByName.emplace(component->name(), component);
}
for (size_t i = 0; i < entity->componentCount(); ++i) {
indexComponents(entity->component(i));
}
}

ComponentPtr Parser::ParserImpl::findComponentByName(const std::string &name) const
{
auto it = mComponentsByName.find(name);
return (it != mComponentsByName.end()) ? it->second : nullptr;
}

void Parser::ParserImpl::loadConnection(const ModelPtr &model, const XmlNodePtr &node, NamePairList &usedConnections)
{
// Define types for variable and component pairs, and their identifiers.
Expand Down Expand Up @@ -1294,9 +1338,8 @@ void Parser::ParserImpl::loadConnection(const ModelPtr &model, const XmlNodePtr
ComponentPtr component1 = nullptr;
ComponentPtr component2 = nullptr;
// Now check the objects exist in the model. TODO Remove as is validation?
if (model->containsComponent(componentNamePair.first)) {
component1 = model->component(componentNamePair.first);
} else {
component1 = findComponentByName(componentNamePair.first);
if (component1 == nullptr) {
if (!component1Missing) {
auto issue = Issue::IssueImpl::create();
issue->mPimpl->setDescription("Connection in model '" + model->name() + "' specifies '" + componentNamePair.first + "' as component_1 but it does not exist in the model.");
Expand All @@ -1305,9 +1348,8 @@ void Parser::ParserImpl::loadConnection(const ModelPtr &model, const XmlNodePtr
addIssue(issue);
}
}
if (model->containsComponent(componentNamePair.second)) {
component2 = model->component(componentNamePair.second);
} else {
component2 = findComponentByName(componentNamePair.second);
if (component2 == nullptr) {
if (!component2Missing) {
auto issue = Issue::IssueImpl::create();
issue->mPimpl->setDescription("Connection in model '" + model->name() + "' specifies '" + componentNamePair.second + "' as component_2 but it does not exist in the model.");
Expand Down
3 changes: 3 additions & 0 deletions tests/component/encapsulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,9 @@ TEST(Encapsulation, encapsulatedComponentMethods)

// Contains component
EXPECT_TRUE(c->containsComponent("comp5"));
c->addComponent(c4n);
EXPECT_TRUE(c->containsComponent("comp3"));
c->removeComponent(c4n);
// Get component
EXPECT_EQ(c4, c->component("comp4"));
const libcellml::ComponentPtr constC4 = c->component("comp4");
Expand Down
1 change: 1 addition & 0 deletions tests/coverage/coverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ TEST(Coverage, analyserAreEquivalentVariables)
EXPECT_FALSE(analyserModel->areEquivalentVariables(variable, nullptr));
auto otherVariable = libcellml::Variable::create("other");
EXPECT_FALSE(analyserModel->areEquivalentVariables(variable, otherVariable));
EXPECT_FALSE(analyserModel->areEquivalentVariables(otherVariable, variable));
}

void checkAstTypeAsString(const libcellml::AnalyserEquationAstPtr &ast)
Expand Down
Loading