Skip to content
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

Proof caching: use dependency graph to increase hit rate #3305

Merged
merged 42 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
eb2b772
Move caching extension to gradle module
FliegendeWurst Oct 19, 2023
df58f4f
Use dependency graph information in caching
FliegendeWurst Oct 19, 2023
77140f2
Handle slicing data in cache copy
FliegendeWurst Oct 19, 2023
8c2ee38
Drop always-false instanceof check in constructor
FliegendeWurst Oct 23, 2023
c6390c7
Optimize dependency graph based sequent reduction
FliegendeWurst Oct 23, 2023
472f71a
SubtreeIterator: adhere to Iterator spec
FliegendeWurst Oct 23, 2023
abc94d0
Implement chain shortening in dependency graph
FliegendeWurst Oct 23, 2023
25ba99b
Merge remote-tracking branch 'origin/main' into caching-sliced
FliegendeWurst Oct 24, 2023
27a15d0
Merge remote-tracking branch 'origin/main' into caching-sliced
FliegendeWurst Oct 26, 2023
7e90a72
Test case (WIP)
FliegendeWurst Oct 29, 2023
1262ab4
Merge remote-tracking branch 'origin/main' into caching-sliced
FliegendeWurst Oct 30, 2023
0952001
Fix test case
FliegendeWurst Oct 30, 2023
f10fa9d
Remove debug print
FliegendeWurst Oct 30, 2023
5230459
Move over test files into keyext.caching
FliegendeWurst Oct 30, 2023
3cfd950
Fix testcase location
FliegendeWurst Nov 2, 2023
f4850f1
Merge remote-tracking branch 'origin/main' into caching-sliced
FliegendeWurst Nov 4, 2023
1dd5f47
Fix slicing of proofs with certain filenames
FliegendeWurst Nov 4, 2023
5c69849
Dependency Graph: tooltips for rendering modifiers
FliegendeWurst Nov 4, 2023
235a361
Merge remote-tracking branch 'origin/main' into caching-sliced
FliegendeWurst Nov 12, 2023
f3ae955
Chain shortening: more useful edge labels
FliegendeWurst Jan 11, 2024
61f29ca
Better tooltip for shortened chains
FliegendeWurst Jan 11, 2024
c795ff8
Merge remote-tracking branch 'origin/main' into caching-sliced
FliegendeWurst Jan 11, 2024
bfcdbca
Javadocs
FliegendeWurst Jan 11, 2024
f66241d
Codestyle
FliegendeWurst Jan 15, 2024
072ece1
Global disable for proof caching
FliegendeWurst Jan 15, 2024
c4b57fd
Refactor action into own package
FliegendeWurst Jan 15, 2024
58c8af6
Refactor CopyReferencedProof action into own class
FliegendeWurst Jan 15, 2024
2fbfe99
New action to re-open goals closed by cache
FliegendeWurst Jan 15, 2024
64453d3
Fix ProgramMethodFinder + javadocs
FliegendeWurst Jan 16, 2024
97633a3
Merge remote-tracking branch 'origin/main' into caching-sliced
FliegendeWurst Jan 18, 2024
5e3a0ae
Set tooltip of proof caching button
FliegendeWurst Jan 18, 2024
aa9ceec
Naming of cached goal re-open action
FliegendeWurst Jan 18, 2024
fab4ab7
Merge remote-tracking branch 'origin/main' into caching-sliced
FliegendeWurst Jan 25, 2024
f31d248
Handle prunes of proof cache targets
FliegendeWurst Jan 25, 2024
9c07e2c
Caching: make behaviour when reference proof is pruned configurable
FliegendeWurst Jan 29, 2024
d199118
Document other caching option
FliegendeWurst Jan 29, 2024
fa8471d
Move caching settings to extension package
FliegendeWurst Jan 29, 2024
910e32e
New proof context menu action to cache open goals
FliegendeWurst Jan 31, 2024
ba3bae1
Transparent proof caching icon
FliegendeWurst Feb 6, 2024
72da2a0
Fix caching bug where an exception was thrown
FliegendeWurst Feb 8, 2024
f4eae66
Make caching more robust by catching exceptions
FliegendeWurst Feb 8, 2024
82b0ac9
Merge remote-tracking branch 'origin/main' into caching-sliced
FliegendeWurst Feb 8, 2024
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
35 changes: 31 additions & 4 deletions key.core/src/main/java/de/uka/ilkd/key/logic/Semisequent.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-only */
package de.uka.ilkd.key.logic;

import java.util.Collection;
import java.util.Iterator;

import org.key_project.util.collection.ImmutableList;
Expand All @@ -27,11 +28,10 @@ private Semisequent() {
seqList = ImmutableSLList.nil();
}


/**
* creates a new Semisequent with the Semisequent elements in seqList; the provided list must be
* redundancy free, i.e., the created sequent must be exactly the same as when creating the
* sequent by subsequently inserting all formulas
* Create a new Semisequent from an ordered collection of formulas.
* The provided list must be redundancy free, i.e., the created sequent must be exactly
* the same as when creating the sequent by subsequently inserting all formulas
*
* @param seqList list of sequent formulas
*/
Expand All @@ -40,6 +40,33 @@ public Semisequent(ImmutableList<SequentFormula> seqList) {
this.seqList = seqList;
}

/**
* Create a new Semisequent from an ordered collection of formulas.
* The provided collection must be redundancy free, i.e., the created sequent must be exactly
* the same as when creating the sequent by subsequently inserting all formulas
*
* @param seqList list of sequent formulas
*/
public Semisequent(Collection<SequentFormula> seqList) {
assert !seqList.isEmpty();
this.seqList = ImmutableList.fromList(seqList);
}

/**
* Create a new Semisequent from an ordered collection of formulas (possibly empty).
* The provided collection must be redundancy free, i.e., the created sequent must be exactly
* the same as when creating the
* sequent by subsequently inserting all formulas.
*
* @param seqList list of sequent formulas
*/
public static Semisequent create(Collection<SequentFormula> seqList) {
if (seqList.isEmpty()) {
return EMPTY_SEMISEQUENT;
}
return new Semisequent(seqList);
}


/**
* creates a new Semisequent with the Semisequent elements in seqList
Expand Down
2 changes: 1 addition & 1 deletion key.core/src/main/java/de/uka/ilkd/key/proof/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ public Collection<Node> children() {
}

/**
* @return an iterator for all nodes in the subtree.
* @return an iterator for all nodes in the subtree (including this node).
*/
public Iterator<Node> subtreeIterator() {
return new SubtreeIterator(this);
Expand Down
2 changes: 1 addition & 1 deletion key.core/src/main/java/de/uka/ilkd/key/proof/Proof.java
Original file line number Diff line number Diff line change
Expand Up @@ -1466,7 +1466,7 @@ public void copyCachedGoals(Proof referencedFrom, Consumer<Integer> callbackTota
ClosedBy c = g.node().lookup(ClosedBy.class);
g.node().deregister(c, ClosedBy.class);
try {
new CopyingProofReplayer(c.proof(), this).copy(c.node(), g);
new CopyingProofReplayer(c.proof(), this).copy(c.node(), g, c.nodesToSkip());
} catch (IntermediateProofReplayer.BuiltInConstructionException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package de.uka.ilkd.key.proof;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
* Iterator over subtree. Current implementation iteratively traverses the tree depth-first.
Expand Down Expand Up @@ -58,6 +59,8 @@ public Node next() {
Node s = nextSibling(n);
if (s != null) {
n = s;
} else {
throw new NoSuchElementException();
}
} else {
n = n.child(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* SPDX-License-Identifier: GPL-2.0-only */
package de.uka.ilkd.key.proof.reference;

import java.util.Set;

import de.uka.ilkd.key.proof.Node;
import de.uka.ilkd.key.proof.Proof;

Expand All @@ -11,7 +13,8 @@
*
* @param proof The proof referenced.
* @param node The node referenced.
* @param nodesToSkip If the referenced proof has dependency graph information: proof steps to skip.
* @author Arne Keller
*/
public record ClosedBy(Proof proof, Node node) {
public record ClosedBy(Proof proof, Node node, Set<Node> nodesToSkip) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Set;

import de.uka.ilkd.key.proof.Goal;
import de.uka.ilkd.key.proof.Node;
Expand Down Expand Up @@ -35,24 +36,36 @@ public CopyingProofReplayer(Proof originalProof, Proof proof) {
*
* @param originalNode original proof
* @param newNode open goal in new proof
* @param skippedNodes nodes to skip when copying
* @throws IntermediateProofReplayer.BuiltInConstructionException on error
*/
public void copy(Node originalNode, Goal newNode)
public void copy(Node originalNode, Goal newNode, Set<Node> skippedNodes)
throws IntermediateProofReplayer.BuiltInConstructionException {
newNode.proof().reOpenGoal(newNode);
newNode.proof().register(this, CopyingProofReplayer.class);
newNode.proof().setMutedProofCloseEvents(true);
OneStepSimplifier.refreshOSS(newNode.proof());

// nodeQueue: nodes in the ORIGINAL proof
Deque<Node> nodeQueue = new ArrayDeque<>();
// queue: nodes in the NEW proof
Deque<Goal> queue = new ArrayDeque<>();

queue.add(newNode);
nodeQueue.add(originalNode);
while (!nodeQueue.isEmpty()) {
while (!nodeQueue.isEmpty() && !queue.isEmpty()) {
Node nextNode = nodeQueue.pop();
Goal nextGoal = queue.pop();
for (int i = nextNode.childrenCount() - 1; i >= 0; i--) {
nodeQueue.addFirst(nextNode.child(i));
}
// skip explicitly requested nodes
if (skippedNodes.contains(nextNode)) {
if (!nextGoal.node().isClosed()) {
queue.addFirst(nextGoal);
}
continue;
}
// skip nextNode if it is a closed goal
if (nextNode.getAppliedRuleApp() == null) {
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* SPDX-License-Identifier: GPL-2.0-only */
package de.uka.ilkd.key.logic;

import java.util.ArrayList;

import de.uka.ilkd.key.logic.op.Function;
import de.uka.ilkd.key.logic.sort.Sort;
import de.uka.ilkd.key.logic.sort.SortImpl;
Expand Down Expand Up @@ -306,4 +308,11 @@ public void testListReplaceAddRedundantList() {
assertEquals(expected, extract(sci), "Both semisequents should be equal.");
}

@Test
void constructorTest() {
var a = Semisequent.EMPTY_SEMISEQUENT;
var b = Semisequent.create(new ArrayList<>());
assertSame(a, b);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package de.uka.ilkd.key.proof.replay;

import java.io.File;
import java.util.HashSet;

import de.uka.ilkd.key.control.DefaultUserInterfaceControl;
import de.uka.ilkd.key.control.KeYEnvironment;
Expand Down Expand Up @@ -45,7 +46,7 @@ void testJavaProof() throws Exception {
proof2.pruneProof(proof2.root());
proof2.getServices().resetCounters();
new CopyingProofReplayer(proof1, proof2).copy(proof1.root(),
proof2.getOpenGoal(proof2.root()));
proof2.getOpenGoal(proof2.root()), new HashSet<>());

Assertions.assertTrue(proof2.closed());
Assertions.assertEquals(proof1.countNodes(), proof2.countNodes());
Expand Down
1 change: 1 addition & 0 deletions key.ui/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies {
api 'org.key-project:docking-frames-core:1.1.3p1'

runtimeOnly project(":keyext.ui.testgen")
runtimeOnly project(":keyext.caching")
runtimeOnly project(":keyext.exploration")
runtimeOnly project(":keyext.slicing")
runtimeOnly project(":keyext.proofmanagement")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
import de.uka.ilkd.key.gui.configuration.Config;
import de.uka.ilkd.key.gui.fonticons.IconFactory;
import de.uka.ilkd.key.gui.notification.events.GeneralInformationEvent;
import de.uka.ilkd.key.gui.plugins.caching.DefaultReferenceSearchDialogListener;
import de.uka.ilkd.key.gui.plugins.caching.ReferenceSearchDialog;
import de.uka.ilkd.key.proof.Node;
import de.uka.ilkd.key.proof.Proof;
import de.uka.ilkd.key.proof.Statistics;
Expand Down Expand Up @@ -315,6 +313,8 @@ private void init(MainWindow mainWindow, String stats) {
buttonPane2.add(saveButton);
buttonPane2.add(saveBundleButton);

// spotless:off
/*
if (proof.closedGoals().stream()
.anyMatch(g -> g.node().lookup(ClosedBy.class) != null)) {
JButton copyReferences = new JButton("Realize cached branches");
Expand All @@ -332,6 +332,8 @@ private void init(MainWindow mainWindow, String stats) {
});
buttonPane2.add(copyReferences);
}
*/
// spotless:on

getRootPane().setDefaultButton(okButton);
getRootPane().addKeyListener(new KeyAdapter() {
Expand Down
6 changes: 6 additions & 0 deletions keyext.caching/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
dependencies {
implementation project(":key.core")
implementation project(":key.ui")

implementation project(":keyext.slicing")
}
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,8 @@ public void actionPerformed(ActionEvent e) {
Goal current = node.proof().getClosedGoal(node);
try {
mediator.stopInterface(true);
new CopyingProofReplayer(c.proof(), node.proof()).copy(c.node(), current);
new CopyingProofReplayer(c.proof(), node.proof()).copy(c.node(), current,
c.nodesToSkip());
mediator.startInterface(true);
} catch (Exception ex) {
LOGGER.error("failed to copy proof ", ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
import de.uka.ilkd.key.rule.NoPosTacletApp;
import de.uka.ilkd.key.rule.merge.CloseAfterMerge;

import org.key_project.slicing.DependencyTracker;
import org.key_project.slicing.analysis.AnalysisResults;

/**
* Utility class for proof caching.
*
Expand Down Expand Up @@ -88,6 +91,8 @@ public static ClosedBy findPreviousProof(DefaultListModel<Proof> previousProofs,
}
return n;
}).filter(Objects::nonNull).collect(Collectors.toCollection(ArrayDeque::new));
var depTracker = p.lookup(DependencyTracker.class);
AnalysisResults results = depTracker != null ? depTracker.analyze(true, false) : null;
while (!nodesToCheck.isEmpty()) {
// for each node, check that the sequent in the reference is
// a subset of the new sequent
Expand All @@ -104,14 +109,27 @@ public static ClosedBy findPreviousProof(DefaultListModel<Proof> previousProofs,
if (n.parent() != null) {
nodesToCheck.add(n.parent());
}
Semisequent ante = n.sequent().antecedent();
Semisequent succ = n.sequent().succedent();
Sequent seq = n.sequent();
if (results != null) {
seq = results.reduceSequent(n);
}
Semisequent ante = seq.antecedent();
Semisequent succ = seq.succedent();
Semisequent anteNew = newNode.sequent().antecedent();
Semisequent succNew = newNode.sequent().succedent();
if (!containedIn(anteNew, ante) || !containedIn(succNew, succ)) {
continue;
}
return new ClosedBy(p, n);
Set<Node> toSkip = new HashSet<>();
if (results != null) {
// computed skipped nodes by iterating through all nodes
n.subtreeIterator().forEachRemaining(x -> {
if (!results.usefulSteps.contains(x)) {
toSkip.add(x);
}
});
}
return new ClosedBy(p, n, toSkip);
}
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import de.uka.ilkd.key.proof.Proof;
import de.uka.ilkd.key.settings.GeneralSettings;

import org.key_project.slicing.DependencyTracker;
import org.key_project.util.helper.FindResources;

import org.junit.jupiter.api.Test;
Expand All @@ -33,11 +34,11 @@ void testFindsReferenceInSameProof() throws Exception {

KeYEnvironment<DefaultUserInterfaceControl> env =
KeYEnvironment.load(new File(testCaseDirectory,
"../../../../../key.ui/examples/heap/verifyThis15_1_RelaxedPrefix/relax.proof"));
"../key.ui/examples/heap/verifyThis15_1_RelaxedPrefix/relax.proof"));
Proof p = env.getLoadedProof();
KeYEnvironment<DefaultUserInterfaceControl> env2 =
KeYEnvironment.load(new File(testCaseDirectory,
"../../../../../key.ui/examples/heap/verifyThis15_1_RelaxedPrefix/relax.proof"));
"../key.ui/examples/heap/verifyThis15_1_RelaxedPrefix/relax.proof"));
Proof p2 = env2.getLoadedProof();

DefaultListModel<Proof> previousProofs = new DefaultListModel<>();
Expand Down Expand Up @@ -77,6 +78,22 @@ void testFindsReferenceInSameProof() throws Exception {
assertTrue(p.closed());
foundReference.proof().copyCachedGoals(p2, null, null);
assertTrue(p.closed());
assertNotEquals(55, foundReference.serialNr());
// test that copying with slicing information works
new DependencyTracker(p2);
Node n55 = p.findAny(x -> x.serialNr() == 55);
assertTrue(ReferenceSearcher.suitableForCloseByReference(n55));
ClosedBy n55Close = ReferenceSearcher.findPreviousProof(previousProofs, n55);
assertEquals(n55.serialNr(), n55Close.node().serialNr());
assertSame(p2, n55Close.proof());
int previousTotal = p.countNodes();
n55.register(n55Close, ClosedBy.class);
p.pruneProof(n55);
p.closeGoal(p.getOpenGoal(n55));
assertTrue(p.closed());
n55.proof().copyCachedGoals(p2, null, null);
assertTrue(p.closed());
assertEquals(previousTotal - 4, p.countNodes());

GeneralSettings.noPruningClosed = true;
p.dispose();
Expand Down
2 changes: 0 additions & 2 deletions keyext.slicing/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
description "Computiation of the proof core (the essential rule applications to close a proof)"

dependencies {
implementation project(":key.core")
implementation project(":key.ui")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ public DependencyTracker(Proof proof) {
if (!SlicingSettingsProvider.getSlicingSettings().getAlwaysTrack()) {
return;
}
// exotic use case: registering a dependency tracker after the proof is loaded
if (proof.countNodes() > 1) {
graph.ensureProofIsTracked(proof);
}
proof.addRuleAppListener(this);
}

Expand Down Expand Up @@ -431,10 +435,12 @@ public void proofIsBeingPruned(ProofTreeEvent e) {
* See {@link DotExporter}.
*
* @param abbreviateFormulas whether to shorten formula labels
* @param abbreviateChains whether to reduce long chains to one link
* @return DOT string
*/
public String exportDot(boolean abbreviateFormulas) {
return DotExporter.exportDot(proof, graph, analysisResults, abbreviateFormulas);
public String exportDot(boolean abbreviateFormulas, boolean abbreviateChains) {
return DotExporter.exportDot(proof, abbreviateChains ? graph.removeChains() : graph,
analysisResults, abbreviateFormulas);
}

/**
Expand Down
Loading