Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ object UtSettings {
/**
* Set to true to start fuzzing if symbolic execution haven't return anything
*/
var useFuzzing: Boolean by getBooleanProperty(true)
var useFuzzing: Boolean by getBooleanProperty(false)

/**
* Set the total attempts to improve coverage by fuzzer.
Expand Down
3 changes: 0 additions & 3 deletions utbot-summary/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ dependencies {
api project(':utbot-framework-api')
compile(project(':utbot-instrumentation'))

implementation group: 'com.github.haifengl', name: 'smile-kotlin', version: '2.6.0'
implementation group: 'com.github.haifengl', name: 'smile-core', version: '2.6.0'

implementation group: 'io.github.microutils', name: 'kotlin-logging', version: kotlin_logging_version

implementation group: 'com.github.javaparser', name: 'javaparser-core', version: '3.22.1'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
package org.utbot.summary.clustering

import org.utbot.framework.plugin.api.Step
import smile.math.distance.Distance

class ExecutionDistance : Distance<Iterable<Step>> {
override fun d(x: Iterable<Step>, y: Iterable<Step>): Double {
return compareTwoPaths(x, y)
}
import org.utbot.summary.clustering.dbscan.Metric

class ExecutionMetric : Metric<Iterable<Step>> {
/**
* Minimum Edit Distance
*/
Expand All @@ -31,4 +27,8 @@ class ExecutionDistance : Distance<Iterable<Step>> {
private fun distance(stmt1: Step, stmt2: Step): Int {
return if (stmt1 == stmt2) 0 else 2
}

override fun compute(object1: Iterable<Step>, object2: Iterable<Step>): Double {
return compareTwoPaths(object1, object2)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package org.utbot.summary.clustering
import org.utbot.framework.plugin.api.Step
import org.utbot.framework.plugin.api.UtExecution
import org.utbot.summary.UtSummarySettings
import smile.clustering.dbscan
import org.utbot.summary.clustering.dbscan.DBSCANTrainer
import org.utbot.summary.clustering.dbscan.neighbor.LinearRangeQuery

class MatrixUniqueness(executions: List<UtExecution>) {

Expand Down Expand Up @@ -85,8 +86,10 @@ class MatrixUniqueness(executions: List<UtExecution>) {
radius: Double = UtSummarySettings.RADIUS_DBSCAN
): Map<Int, List<UtExecution>> {
val executionPaths = methodExecutions.map { it.path.asIterable() }.toTypedArray()
val cluster = dbscan(executionPaths, ExecutionDistance(), minPts, radius)
return methodExecutions.withIndex().groupBy({ cluster.y[it.index] }, { it.value })
val dbscan = DBSCANTrainer(eps = 5.0f, minSamples = 1, metric = ExecutionMetric(), rangeQuery = LinearRangeQuery())
val dbscanModel = dbscan.fit(executionPaths)
val clusterLabels = dbscanModel.clusterLabels
return methodExecutions.withIndex().groupBy({ clusterLabels[it.index] }, { it.value })
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.utbot.summary.clustering.dbscan

import org.utbot.summary.clustering.dbscan.neighbor.RangeQuery

/**
* @property k
* @property clusterLabels
* @property clusterSizes The number of observations in each cluster.
*/
class DBSCANModel<K>(
val k: Int = 0,
val clusterLabels: IntArray,
val clusterSizes: IntArray,
val rangeQuery: RangeQuery<K>,
val eps: Float,
val minSamples: Int
) {
/** Find a cluster for new data. */
/* fun predictCluster(data: K): Int {
val neighbors = rangeQuery.findNeighbors(data, eps)

if(neighbors.size < minSamples) {
return NOISE
}


}*/
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.utbot.summary.clustering.dbscan

import org.utbot.summary.clustering.dbscan.neighbor.LinearRangeQuery
import org.utbot.summary.clustering.dbscan.neighbor.RangeQuery

const val NOISE = -3
const val CLUSTER_PART = -2
const val UNDEFINED = -1

class DBSCANTrainer<T>(val eps: Float, val minSamples: Int, val metric: Metric<T>, val rangeQuery: RangeQuery<T>) {
init {
require(minSamples > 0) { "MinSamples parameter should be more than 0: $minSamples" }
require(eps > 0.0f) { "Eps parameter should be more than 0: $eps" }
}

fun fit(data: Array<T>): DBSCANModel<T> {
if (rangeQuery is LinearRangeQuery) {
rangeQuery.data = data
rangeQuery.metric = metric
} // TODO: could be refactored if we add some new variants of RangeQuery

val numberOfClusters = 0
val labels = IntArray(data.size) { _ -> UNDEFINED }
val clusterSizes = IntArray(numberOfClusters)

var k = 0 // cluster index
for (i in data.indices) {
if(labels[i] == UNDEFINED) {
val neigbors = rangeQuery.findNeighbors(data[i], eps).toMutableList()
if (neigbors.size < minSamples) {
labels[i] = NOISE
} else {
k++
labels[i] = k
// expand cluster
neigbors.forEach { // Neighbors to expand
if(labels[it.index] == UNDEFINED) {
labels[it.index] = CLUSTER_PART // all neighbors of a cluster point became cluster points
}
}

for (j in neigbors.indices) { // Process every seed point Q
val q = neigbors[j]
val idx = q.index


if (labels[idx] == NOISE) { // Change Noise to border point
labels[idx] = k
}

if (labels[idx] == UNDEFINED || labels[idx] == CLUSTER_PART) {
labels[idx] = k


val qNeighbors = rangeQuery.findNeighbors(q.key, eps)

if (qNeighbors.size >= minSamples) { // Density check (if Q is a core point)
// merge two cluster parts
for (qNeighbor in qNeighbors) {
val label = labels[qNeighbor.index]
if (label == UNDEFINED) {
labels[qNeighbor.index] = CLUSTER_PART
}

if (label == UNDEFINED || label == NOISE) {
neigbors.add(qNeighbor)
}
}
}
}
}
}

}
}

return DBSCANModel(k = numberOfClusters, clusterLabels = labels, clusterSizes = clusterSizes, rangeQuery = rangeQuery, eps = eps, minSamples = minSamples)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.utbot.summary.clustering.dbscan


interface Metric<T> {
fun compute(object1: T, object2: T): Double
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.utbot.summary.clustering.dbscan.neighbor

import org.utbot.summary.clustering.dbscan.Metric

class LinearRangeQuery <K> (): RangeQuery<K> {
lateinit var data: Array<K>
lateinit var metric: Metric<K>

override fun findNeighbors(queryKey: K, radius: Float): List<Neighbor<K>> {
val neighbors = mutableListOf<Neighbor<K>>()
data.forEachIndexed { index, point ->
val distance = metric.compute(queryKey, point)
if (distance <= radius && queryKey != point) {
neighbors.add(Neighbor(point, index, distance))
}
}

return neighbors
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.utbot.summary.clustering.dbscan.neighbor

class Neighbor<K>(val key: K, val index: Int, val distance: Double): Comparable<Neighbor<K>> {
override fun compareTo(other: Neighbor<K>): Int {
val distance = distance.compareTo(other.distance)
return if (distance == 0) index.compareTo(other.index) else distance
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.utbot.summary.clustering.dbscan.neighbor

interface RangeQuery<K> {
fun findNeighbors(queryKey: K, radius: Float): List<Neighbor<K>>
}