-
Notifications
You must be signed in to change notification settings - Fork 117
Wire Neo4j heuristics into the search fitness function #1731
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
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
8f5e81c
wire neo4j heuristics into the search fitness function
andyfelder16 5e69c02
Merge branch 'neo4j-graph-reader' into neo4j-fitness-integration
andyfelder16 ce64642
Merge branch 'neo4j-graph-reader' into neo4j-fitness-integration
andyfelder16 edce4cc
Merge branch 'neo4j-graph-reader' into neo4j-fitness-integration
andyfelder16 8929d2d
Merge branch 'neo4j-graph-reader' into neo4j-fitness-integration
andyfelder16 1fb8dc6
merge master into the neo4j fitness integration alongside the dynamod…
andyfelder16 f67a9f1
document the neo4j heuristics handler and drop the redundant prefixes…
andyfelder16 ce624d2
Merge remote-tracking branch 'origin/master' into neo4j-fitness-integ…
andyfelder16 9cb092f
Merge remote-tracking branch 'origin/master' into neo4j-fitness-integ…
andyfelder16 15974b1
report the calculator's maximum distance when a neo4j heuristic fails…
andyfelder16 1bfc0e3
a failed neo4j heuristic now reports Double.MAX_VALUE, dropping the >…
andyfelder16 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
...java/org/evomaster/client/java/controller/internal/db/neo4j/Neo4jCommandWithDistance.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package org.evomaster.client.java.controller.internal.db.neo4j; | ||
|
|
||
| /** | ||
| * Pairs a captured Cypher query with its computed distance to being satisfied by the live graph. | ||
| */ | ||
| public final class Neo4jCommandWithDistance { | ||
|
|
||
| private final String command; | ||
| private final Neo4jDistanceWithMetrics distanceWithMetrics; | ||
|
|
||
| /** | ||
| * Creates the evaluation of one captured query. | ||
| * | ||
| * @param command the Cypher query, as executed by the SUT | ||
| * @param distanceWithMetrics its heuristic result | ||
| */ | ||
| public Neo4jCommandWithDistance(String command, Neo4jDistanceWithMetrics distanceWithMetrics) { | ||
| this.command = command; | ||
| this.distanceWithMetrics = distanceWithMetrics; | ||
| } | ||
|
|
||
| /** | ||
| * @return the Cypher query, as executed by the SUT | ||
| */ | ||
| public String getCommand() { | ||
| return command; | ||
| } | ||
|
|
||
| /** | ||
| * @return the heuristic result of the query | ||
| */ | ||
| public Neo4jDistanceWithMetrics getDistanceWithMetrics() { | ||
| return distanceWithMetrics; | ||
| } | ||
| } |
64 changes: 64 additions & 0 deletions
64
...java/org/evomaster/client/java/controller/internal/db/neo4j/Neo4jDistanceWithMetrics.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package org.evomaster.client.java.controller.internal.db.neo4j; | ||
|
|
||
| import org.evomaster.client.java.controller.neo4j.heuristics.Neo4jHeuristicsCalculator; | ||
|
|
||
| /** | ||
| * Result of scoring one captured Cypher query against the live graph: the distance to satisfying the | ||
| * query, how many graph nodes were considered, and whether the evaluation failed. | ||
| * <p> | ||
| * The distance is {@code 1 - ofTrue} of the {@code Truthness} computed by the heuristics calculator, so a | ||
| * computed value lies in {@code [0,1]} by construction, with 0 meaning the query is satisfied. A failed | ||
| * evaluation carries {@link Neo4jHeuristicsCalculator#MAX_NEO4J_DISTANCE}. | ||
| */ | ||
| public final class Neo4jDistanceWithMetrics { | ||
|
|
||
| private final double distance; | ||
| private final int numberOfEvaluatedNodes; | ||
| private final boolean evaluationFailure; | ||
|
|
||
| /** | ||
| * Creates a Neo4j heuristic result. | ||
| * | ||
| * @param distance distance to satisfying the query, 0 meaning satisfied | ||
| * @param numberOfEvaluatedNodes number of graph nodes considered | ||
| * @param evaluationFailure whether the evaluation failed, in which case the distance must be | ||
| * {@link Neo4jHeuristicsCalculator#MAX_NEO4J_DISTANCE} | ||
| */ | ||
| public Neo4jDistanceWithMetrics(double distance, int numberOfEvaluatedNodes, boolean evaluationFailure) { | ||
| if (distance < 0.0d || Double.isNaN(distance)) { | ||
| throw new IllegalArgumentException("distance must be non-negative, but was " + distance); | ||
| } | ||
| if (numberOfEvaluatedNodes < 0) { | ||
| throw new IllegalArgumentException("numberOfEvaluatedNodes must be non-negative"); | ||
| } | ||
| if (evaluationFailure && distance != Neo4jHeuristicsCalculator.MAX_NEO4J_DISTANCE) { | ||
| throw new IllegalArgumentException( | ||
| "a failed Neo4j distance computation cannot have a value different than MAX_NEO4J_DISTANCE"); | ||
| } | ||
| this.distance = distance; | ||
| this.numberOfEvaluatedNodes = numberOfEvaluatedNodes; | ||
| this.evaluationFailure = evaluationFailure; | ||
| } | ||
|
|
||
| /** | ||
| * @return distance to satisfying the query, 0 meaning satisfied, | ||
| * {@link Neo4jHeuristicsCalculator#MAX_NEO4J_DISTANCE} on failure | ||
| */ | ||
| public double getDistance() { | ||
| return distance; | ||
| } | ||
|
|
||
| /** | ||
| * @return number of graph nodes considered | ||
| */ | ||
| public int getNumberOfEvaluatedNodes() { | ||
| return numberOfEvaluatedNodes; | ||
| } | ||
|
|
||
| /** | ||
| * @return whether the evaluation failed | ||
| */ | ||
| public boolean isEvaluationFailure() { | ||
| return evaluationFailure; | ||
| } | ||
| } | ||
145 changes: 145 additions & 0 deletions
145
...er/src/main/java/org/evomaster/client/java/controller/internal/db/neo4j/Neo4jHandler.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| package org.evomaster.client.java.controller.internal.db.neo4j; | ||
|
|
||
| import org.evomaster.client.java.controller.neo4j.data.Neo4jGraph; | ||
| import org.evomaster.client.java.controller.neo4j.heuristics.Neo4jHeuristicsCalculator; | ||
| import org.evomaster.client.java.controller.neo4j.operations.MatchOperation; | ||
| import org.evomaster.client.java.controller.neo4j.parser.CypherParser; | ||
| import org.evomaster.client.java.controller.neo4j.parser.CypherParserException; | ||
| import org.evomaster.client.java.controller.neo4j.parser.CypherParserFactory; | ||
| import org.evomaster.client.java.controller.internal.TaintHandlerExecutionTracer; | ||
| import org.evomaster.client.java.instrumentation.Neo4JRunCommand; | ||
| import org.evomaster.client.java.utils.SimpleLogger; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Acts upon Cypher queries executed by the SUT (captured as {@link Neo4JRunCommand}s): for each | ||
| * captured query it computes how close the live graph is to satisfying it, as a distance to minimize. | ||
| * Only MATCH queries are scored; a query that does not parse as a MATCH (e.g. a write) is skipped. | ||
| */ | ||
| public class Neo4jHandler { | ||
|
|
||
| /** Cypher queries captured from {@code Session.run}, pending evaluation. */ | ||
| private final List<Neo4JRunCommand> operations; | ||
|
|
||
| /** The computed heuristics, one per scored query. */ | ||
| private final List<Neo4jCommandWithDistance> commandsWithDistances; | ||
|
|
||
| /** Whether to compute heuristics based on execution or not. */ | ||
| private volatile boolean calculateHeuristics; | ||
|
|
||
| /** | ||
| * The SUT's {@code org.neo4j.driver.Driver}, kept as an {@code Object} and used by reflection so | ||
| * we do not hard-depend on a specific driver version. {@code null} when the SUT does not use Neo4j. | ||
| */ | ||
| private Object neo4jConnection = null; | ||
|
|
||
| private final CypherParser parser = CypherParserFactory.buildParser(); | ||
|
|
||
| private final Neo4jHeuristicsCalculator calculator = | ||
| new Neo4jHeuristicsCalculator(new TaintHandlerExecutionTracer()); | ||
| private final Neo4jGraphReader graphReader = new Neo4jGraphReader(); | ||
|
|
||
| /** | ||
| * Creates a handler with heuristic calculation enabled. | ||
| */ | ||
| public Neo4jHandler() { | ||
| operations = new ArrayList<>(); | ||
| commandsWithDistances = new ArrayList<>(); | ||
| calculateHeuristics = true; | ||
| } | ||
|
|
||
| /** | ||
| * Clears data collected for the current action. | ||
| */ | ||
| public void reset() { | ||
| operations.clear(); | ||
| commandsWithDistances.clear(); | ||
| } | ||
|
|
||
| /** | ||
| * @return whether Neo4j heuristic calculation is enabled | ||
| */ | ||
| public boolean isCalculateHeuristics() { | ||
| return calculateHeuristics; | ||
| } | ||
|
|
||
| /** | ||
| * Enables or disables Neo4j heuristic calculation. | ||
| * | ||
| * @param calculateHeuristics new calculation state | ||
| */ | ||
| public void setCalculateHeuristics(boolean calculateHeuristics) { | ||
| this.calculateHeuristics = calculateHeuristics; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the driver used to read the live graph. | ||
| * | ||
| * @param neo4jConnection the SUT's {@code org.neo4j.driver.Driver}, or {@code null} if it has none | ||
| */ | ||
| public void setNeo4jConnection(Object neo4jConnection) { | ||
| this.neo4jConnection = neo4jConnection; | ||
| } | ||
|
|
||
| /** | ||
| * Registers one intercepted Cypher query. | ||
| * | ||
| * @param info intercepted query | ||
| */ | ||
| public void handle(Neo4JRunCommand info) { | ||
| if (calculateHeuristics && info.getQuery() != null) { | ||
| operations.add(info); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Evaluates all registered queries against a single snapshot of the graph, and consumes them. | ||
| * The snapshot is read once per action rather than per query, since the SUT is not running while | ||
| * the heuristics are computed. | ||
| * | ||
| * @return evaluated queries for the current action | ||
| */ | ||
| public List<Neo4jCommandWithDistance> getEvaluatedNeo4jCommands() { | ||
|
|
||
| if (!calculateHeuristics || neo4jConnection == null || operations.isEmpty()) { | ||
| operations.clear(); | ||
| return commandsWithDistances; | ||
| } | ||
|
|
||
| Neo4jGraph graph; | ||
| try { | ||
| graph = graphReader.read(neo4jConnection); | ||
| } catch (Exception e) { | ||
| SimpleLogger.uniqueWarn("Failed to read the Neo4j graph to compute heuristics: " + e.getMessage()); | ||
| operations.clear(); | ||
| return commandsWithDistances; | ||
| } | ||
|
|
||
| for (Neo4JRunCommand op : operations) { | ||
| String query = op.getQuery(); | ||
| final MatchOperation parsedQuery; | ||
| try { | ||
| parsedQuery = parser.parse(query); | ||
| } catch (CypherParserException e) { | ||
| SimpleLogger.uniqueWarn("Failed to parse Cypher query for Neo4j heuristics: " + e.getMessage()); | ||
| continue; | ||
| } | ||
|
|
||
| Neo4jDistanceWithMetrics metrics; | ||
| try { | ||
| double distance = calculator.computeDistance(parsedQuery, graph); | ||
| metrics = new Neo4jDistanceWithMetrics(distance, graph.nodeCount(), false); | ||
| } catch (Exception e) { | ||
| SimpleLogger.uniqueWarn("Failed to compute Neo4j heuristic for query: " + query | ||
| + " | cause: " + e.getClass().getName() + ": " + e.getMessage()); | ||
| metrics = new Neo4jDistanceWithMetrics(Neo4jHeuristicsCalculator.MAX_NEO4J_DISTANCE, graph.nodeCount(), true); | ||
| } | ||
| commandsWithDistances.add(new Neo4jCommandWithDistance(query, metrics)); | ||
| } | ||
|
|
||
| operations.clear(); | ||
| return commandsWithDistances; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if normalized, then the name
getDistanceis confusing.