Skip to content

Fixes to Merging algorithm #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: vara-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2a30dcc
Fixes to merging algorithm to compensate xml formats limitations
May 24, 2021
d3edd74
Fix
May 24, 2021
b4ac257
Fix
May 24, 2021
99f6130
Workaround for getChildren, relationship copy for non strict fixed
May 24, 2021
5d6a33e
Stuff
Jun 1, 2021
c17f473
Eval version
Jun 8, 2021
096cefc
Merge branch 'vara-dev' into f-mergingFixes
vulder Jun 10, 2021
4c94e95
Update include/vara/Feature/FeatureTreeNode.h
vulder Jul 23, 2021
0eba8f7
Merge branch 'vara-dev' into f-mergingFixes
Jul 23, 2021
eb6aabf
Revert merge error
Jul 23, 2021
5975b52
Remove old testcase, reproduced in other tests
Jul 23, 2021
1025d36
Feedback
Jul 30, 2021
25a26e6
Update unittests/Feature/FeatureModelTransaction.cpp
padupr Jul 30, 2021
08c1ae5
Update unittests/Feature/FeatureModelTransaction.cpp
padupr Jul 30, 2021
c4226e5
Feedback
Aug 28, 2021
aed93e3
Merge branch 'vara-dev' into f-mergingFixes
vulder Aug 28, 2021
ee977c9
Revert changes to bindings
Aug 28, 2021
f7e0e52
Revert all changes to bindings
Aug 28, 2021
052c46f
Merge branch 'vara-dev' into f-mergingFixes
vulder Jan 17, 2022
e0f211b
Fixes error
vulder Jan 17, 2022
c4b01e5
Adds error
vulder Feb 4, 2022
df1f5ea
Merge branch 'vara-dev' into f-mergingFixes
vulder Feb 6, 2022
e94701e
Merge branch 'vara-dev' into f-mergingFixes
vulder Feb 12, 2025
49c7967
Adapts constraint removal to new feature-constraint handles
vulder Feb 12, 2025
aea67f2
Updates external dependencies
vulder Feb 12, 2025
87d82c7
Merge branch 'update-dependencies' into f-mergingFixes
vulder Feb 12, 2025
5033222
Removes unnecessary parent lookup
vulder Feb 12, 2025
d087829
Merge branch 'vara-dev' into f-mergingFixes
vulder Feb 13, 2025
2a6e2f9
Merge branch 'vara-dev' into f-mergingFixes
boehmseb Mar 11, 2025
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
9 changes: 9 additions & 0 deletions include/vara/Feature/FeatureModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,15 @@ class FeatureModel {
return Constraints.back().get();
}

void removeConstraint(Constraint *C) {
// TODO se-sic/VaRA#701 implement tree based comparison
Constraints.erase(
std::find_if(Constraints.begin(), Constraints.end(),
[C](const std::unique_ptr<Constraint> &UniC) {
return UniC.get() == C;
}));
}

/// Delete a \a Feature.
void removeFeature(Feature &Feature);

Expand Down
78 changes: 73 additions & 5 deletions include/vara/Feature/FeatureModelTransaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ class FeatureModelTransaction
}
}

void removeConstraint(Constraint &RemoveConstraint) {
this->removeConstraintImpl(RemoveConstraint);
}

void setName(std::string Name) { return this->setNameImpl(std::move(Name)); }

void setCommit(std::string Commit) {
Expand Down Expand Up @@ -253,6 +257,14 @@ class FeatureModelModification {
return FM.addConstraint(std::move(Constraint));
}

static void removeConstraint(FeatureModel &FM, Constraint *R) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Consraint *R here and Constraint *C below?
Do they have different meaning, or should they be named the same?

FM.removeConstraint(R);
}

static void removeConstraint(Feature &F, Constraint *C) {
F.removeConstraintNonPreserve(C);
}

static void setName(FeatureModel &FM, std::string NewName) {
FM.setName(std::move(NewName));
}
Expand Down Expand Up @@ -304,8 +316,12 @@ class AddFeatureToModel : public FeatureModelModification {
return nullptr;
}
if (Parent) {
setParent(*InsertedFeature, *Parent);
addEdge(*Parent, *InsertedFeature);
FeatureTreeNode *ParentNode = Parent;
if (!Parent->getChildren<Relationship>(1).empty()) {
ParentNode = *(Parent->getChildren<Relationship>(1).begin());
}
Comment on lines +326 to +328
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure I understand this correctly:
This is required because we model (xor/or) relationships as separate nodes so the relationship node has to be set as parent if one exists, correct?

This assumes that a Feature can have exactly one (or no) relationship as child and if it has, there are no features as direct children allowed. Is this assumption validated anywhere?
I'm asking because this might become an issue when integrating UVL because UVL does not have these constraints, i.e., a feature can have multiple types of relationships as children at once (I'm not sure how about multiple instances of the same type, e.g., multiple or groups).

setParent(*InsertedFeature, *ParentNode);
addEdge(*ParentNode, *InsertedFeature);
} else if (FM.getRoot()) {
setParent(*InsertedFeature, *FM.getRoot());
addEdge(*FM.getRoot(), *InsertedFeature);
Expand All @@ -322,6 +338,39 @@ class AddFeatureToModel : public FeatureModelModification {
Feature *Parent;
};

//===----------------------------------------------------------------------===//
// RemoveConstraintFromModel
//===----------------------------------------------------------------------===//

class RemoveConstraintFromModel : public FeatureModelModification {
friend class FeatureModelModification;
friend class RemoveFeatureFromModel;

public:
void exec(FeatureModel &FM) override { (*this)(FM); }

void operator()(FeatureModel &FM) {
assert(RemoveConstraint.getParent() == nullptr &&
"Subtree deletion is not supported");
UncoupleVisitor UV;
RemoveConstraint.accept(UV);
removeConstraint(FM, &RemoveConstraint);
}

private:
RemoveConstraintFromModel(Constraint &RemoveConstraint)
: RemoveConstraint(RemoveConstraint) {}

class UncoupleVisitor : public ConstraintVisitor {
public:
void visit(PrimaryFeatureConstraint *C) override {
removeConstraint(*C->getFeature(), C);
}
};

Constraint &RemoveConstraint;
};

//===----------------------------------------------------------------------===//
// RemoveFeatureFromModel
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -365,6 +414,15 @@ class RemoveFeatureFromModel : public FeatureModelModification {
}
}

while (!F->constraints().empty()) {
Constraint *C = *(F->constraints().begin());
while (C->getParent()) {
C = C->getParent();
}
RemoveConstraintFromModel RCFM(*C);
RCFM(FM);
}

removeEdge(*F->getParent(), *F);
removeFeature(FM, *F);
}
Expand Down Expand Up @@ -803,14 +861,19 @@ class FeatureModelCopyTransactionBase {
}

Constraint *addConstraintImpl(std::unique_ptr<Constraint> NewConstraint) {
if (!FM) {
return nullptr;
}
assert(FM && "FeatureModel is null.");

return FeatureModelModification::make_modification<AddConstraintToModel>(
std::move(NewConstraint))(*FM);
}

void removeConstraintImpl(Constraint &RemoveConstraint) {
assert(FM && "FeatureModel is null.");

FeatureModelModification::make_modification<RemoveConstraintFromModel>(
RemoveConstraint)(*FM);
}

void setNameImpl(std::string Name) {
assert(FM && "FeatureModel is null.");

Expand Down Expand Up @@ -919,6 +982,11 @@ class FeatureModelModifyTransactionBase {
AddConstraintToModel>(std::move(NewConstraint)));
}

void removeConstraintImpl(Constraint &RemoveConstraint) {
Modifications.push_back(FeatureModelModification::make_unique_modification<
RemoveConstraintFromModel>(RemoveConstraint));
}

void setNameImpl(std::string Name) {
assert(FM && "FeatureModel is null.");

Expand Down
45 changes: 40 additions & 5 deletions lib/Feature/FeatureModelTransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ std::unique_ptr<Feature> FeatureCopy(Feature &F);

bool CompareProperties(const Feature &F1, const Feature &F2, bool Strict);

std::optional<Relationship *> getFeatureRelationship(Feature &F);

void addFeature(FeatureModel &FM, std::unique_ptr<Feature> NewFeature,
Feature *Parent) {
auto Trans = FeatureModelModifyTransaction::openTransaction(FM);
Expand Down Expand Up @@ -73,7 +75,27 @@ mergeFeatureModels(FeatureModel &FM1, FeatureModel &FM2, bool Strict) {
// Is there a similar Feature in the original FM
if (Feature *CMP = FM.getFeature(F.getName())) {
if (CompareProperties(*CMP, F, Strict)) {
// similar feature, maybe merge locations
// similar feature, maybe add relationship
auto FRelationship = getFeatureRelationship(F);
auto CMPRelationship = getFeatureRelationship(*CMP);
if (FRelationship && CMPRelationship) {
// Relationships must match in strict mode
if (Strict && (FRelationship.value()->getKind() !=
CMPRelationship.value()->getKind())) {
return false;
}
} else if (FRelationship && !CMPRelationship) {
// Relationship is carried over to work around xml format limits
if (CMP->getChildren<Feature>().size() < 2) {
Trans.addRelationship(FRelationship.value()->getKind(),
F.getName().str());
} else if (Strict) {
return false;
}
}
// CMPRelationship is already in the new model

// merge locations
for (FeatureSourceRange const &FSR : F.getLocations()) {
if (std::find(CMP->getLocationsBegin(), CMP->getLocationsEnd(), FSR) ==
CMP->getLocationsEnd()) {
Expand All @@ -89,9 +111,13 @@ mergeFeatureModels(FeatureModel &FM1, FeatureModel &FM2, bool Strict) {
return false;
}
Trans.addFeature(std::move(Copy), F.getParentFeature());
// add relationship to new feature
auto Relationship = getFeatureRelationship(F);
if (Relationship.has_value()) {
Trans.addRelationship(Relationship.value()->getKind(), F.getName().str());
}
}

// copy children if missing
for (Feature *Child : F.getChildren<Feature>()) {
if (!mergeSubtree(Trans, FM, *Child, Strict)) {
return false;
Expand Down Expand Up @@ -124,9 +150,11 @@ mergeFeatureModels(FeatureModel &FM1, FeatureModel &FM2, bool Strict) {

[[nodiscard]] bool CompareProperties(const Feature &F1, const Feature &F2,
bool Strict) {
// name equality
if (F1.getName() != F2.getName()) {
return false;
}
// parent equality, properties checked before so name equality is sufficient
if (F1.getKind() != Feature::FeatureKind::FK_ROOT &&
F2.getKind() != Feature::FeatureKind::FK_ROOT) {
if (*(F1.getParentFeature()) != *(F2.getParentFeature())) {
Expand All @@ -147,9 +175,6 @@ mergeFeatureModels(FeatureModel &FM1, FeatureModel &FM2, bool Strict) {
if (F1.getKind() == Feature::FeatureKind::FK_ROOT) {
return true;
}
if (F1.getParent()->getKind() != F2.getParent()->getKind()) {
return false;
}
if (F1.getKind() == Feature::FeatureKind::FK_BINARY) {
return true;
}
Expand All @@ -161,4 +186,14 @@ mergeFeatureModels(FeatureModel &FM1, FeatureModel &FM2, bool Strict) {
return false;
}

std::optional<Relationship *> getFeatureRelationship(Feature &F) {
if (!F.getChildren<Relationship>(1).empty()) {
auto *C = *(F.children().begin());
if (auto *R = llvm::dyn_cast<Relationship>(C)) {
return R;
}
}
return std::nullopt;
}

} // namespace vara::feature
1 change: 1 addition & 0 deletions lib/Feature/FeatureModelWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ int FeatureModelXmlWriter::writeFeatureModel(std::string Path) {
std::unique_ptr<xmlTextWriter, void (*)(xmlTextWriterPtr)> Writer(
xmlNewTextWriterFilename(Path.data(), 0), &xmlFreeTextWriter);
if (!Writer) {
llvm::errs() << "Could not create filewriter for " << Path << '\n';
return false;
}

Expand Down
16 changes: 16 additions & 0 deletions unittests/Feature/Feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,20 @@ TEST(Feature, locationUpdate) {
EXPECT_EQ(*TestLCO.getLocationsBegin(), Fsr2);
}

TEST(Feature, getChildren) {
FeatureModelBuilder B;
B.makeFeature<NumericFeature>("a", std::vector<int>{1, 2, 3});
B.addEdge("a", "aa")->makeFeature<BinaryFeature>("aa");
B.addEdge("a", "ab")->makeFeature<BinaryFeature>("ab");

B.emplaceRelationship(Relationship::RelationshipKind::RK_ALTERNATIVE, "a");
auto FM = B.buildFeatureModel();
ASSERT_TRUE(FM);

EXPECT_EQ(FM->getFeature("a")->getChildren<Feature>(0).size(), 0);
EXPECT_EQ(FM->getFeature("a")->getChildren<Feature>(1).size(), 0);
EXPECT_EQ(FM->getFeature("a")->getChildren<Feature>(2).size(), 2);
EXPECT_EQ(FM->getFeature("a")->getChildren<Feature>(3).size(), 2);
}

} // namespace vara::feature
28 changes: 28 additions & 0 deletions unittests/Feature/FeatureModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,34 @@ TEST_F(FeatureModelTest, dfs) {
}
}

//===----------------------------------------------------------------------===//
// FeatureModelGetChildren Tests
//===----------------------------------------------------------------------===//

class FeatureModelGetChildrenTest : public ::testing::Test {
protected:
void SetUp() override {
FeatureModelBuilder B;
B.makeFeature<BinaryFeature>("a");
B.addEdge("a", "aa")->makeFeature<BinaryFeature>("aa");
B.addEdge("a", "ab")->makeFeature<BinaryFeature>("ab");
B.emplaceRelationship(Relationship::RelationshipKind::RK_OR, "ab");
B.addEdge("ab", "aba")->makeFeature<BinaryFeature>("aba");
B.addEdge("ab", "abb")->makeFeature<BinaryFeature>("abb");
B.makeFeature<BinaryFeature>("b");
B.emplaceRelationship(Relationship::RelationshipKind::RK_ALTERNATIVE, "b");
B.addEdge("b", "ba")->makeFeature<BinaryFeature>("ba");
B.addEdge("b", "bb")->makeFeature<BinaryFeature>("bb");
B.makeFeature<BinaryFeature>("c");
B.addEdge("c", "ca")->makeFeature<NumericFeature>("ca", std::pair(1, 5));
B.addEdge("c", "cb")->makeFeature<NumericFeature>("cb", std::pair(1, 5));
FM = B.buildFeatureModel();
ASSERT_TRUE(FM);
}

std::unique_ptr<FeatureModel> FM;
};

//===----------------------------------------------------------------------===//
// FeatureModelConsistencyChecker Tests
//===----------------------------------------------------------------------===//
Expand Down
Loading