Kaynağa Gözat

Merge pull request #671 from jplag/normalized-similarity-range

Normalized similarity range
Timur Sağlam 4 yıl önce
ebeveyn
işleme
b4b2b920e1

+ 1 - 1
README.md

@@ -77,7 +77,7 @@ named arguments:
   -x X             All files named in this file will be ignored in the comparison (line-separated list)
   -t T             Tunes the comparison sensitivity by adjusting the minimum token  required  to be counted as a matching section. A smaller
                         <n> increases the sensitivity but might lead to more false-positives
-  -m M             Comparison similarity threshold [0-100]: All comparisons above this threshold will be saved (default: 0.0)
+  -m M             Comparison similarity threshold [0.0-1.0]: All comparisons above this threshold will be saved (default: 0.0)
   -n N             The maximum number of comparisons that will be shown in the  generated report, if set to -1 all comparisons will be shown
                         (default: 30)
   -r R             Name of the directory in which the comparison results will be stored (default: result)

+ 4 - 4
cli/src/test/java/de/jplag/cli/SimiliarityThresholdTest.java

@@ -25,21 +25,21 @@ class SimiliarityThresholdTest extends CommandLineInterfaceTest {
 
     @Test
     void testLowerBound() {
-        String argument = buildArgument(CommandLineArgument.SIMILARITY_THRESHOLD, Double.toString(-1.0));
+        String argument = buildArgument(CommandLineArgument.SIMILARITY_THRESHOLD, Double.toString(-0.01));
         buildOptionsFromCLI(argument, CURRENT_DIRECTORY);
         assertEquals(0.0, options.similarityThreshold(), DELTA);
     }
 
     @Test
     void testUpperBound() {
-        String argument = buildArgument(CommandLineArgument.SIMILARITY_THRESHOLD, Double.toString(101.0));
+        String argument = buildArgument(CommandLineArgument.SIMILARITY_THRESHOLD, Double.toString(1.01));
         buildOptionsFromCLI(argument, CURRENT_DIRECTORY);
-        assertEquals(100.0, options.similarityThreshold(), DELTA);
+        assertEquals(1.0, options.similarityThreshold(), DELTA);
     }
 
     @Test
     void testValidThreshold() {
-        double expectedValue = 50.0;
+        double expectedValue = 0.5;
         String argument = buildArgument(CommandLineArgument.SIMILARITY_THRESHOLD, Double.toString(expectedValue));
         buildOptionsFromCLI(argument, CURRENT_DIRECTORY);
         assertEquals(expectedValue, options.similarityThreshold(), DELTA);

+ 6 - 8
core/src/main/java/de/jplag/JPlagComparison.java

@@ -30,21 +30,21 @@ public record JPlagComparison(Submission firstSubmission, Submission secondSubmi
     }
 
     /**
-     * @return Maximum similarity in percent of both submissions.
+     * @return Maximum similarity in interval [0, 1]. O means no similarity, 1 means maximum similarity.
      */
     public final double maximalSimilarity() {
         return Math.max(similarityOfFirst(), similarityOfSecond());
     }
 
     /**
-     * @return Minimum similarity in percent of both submissions.
+     * @return Minimum similarity in interval [0, 1]. O means no similarity, 1 means maximum similarity.
      */
     public final double minimalSimilarity() {
         return Math.min(similarityOfFirst(), similarityOfSecond());
     }
 
     /**
-     * @return Similarity in percent (what percentage of tokens across both submissions are matched).
+     * @return Average similarity in interval [0, 1]. O means no similarity, 1 means maximum similarity.
      */
     public final double similarity() {
         boolean subtractBaseCode = firstSubmission.hasBaseCodeMatches() && secondSubmission.hasBaseCodeMatches();
@@ -54,8 +54,7 @@ public record JPlagComparison(Submission firstSubmission, Submission secondSubmi
     }
 
     /**
-     * @return Similarity in percent for the first submission (what percent of the first submission is similar to the
-     * second).
+     * @return Similarity of the first submission in interval [0, 1]. O means no similarity, 1 means maximum similarity.
      */
     public final double similarityOfFirst() {
         int divisor = firstSubmission.getSimilarityDivisor(true);
@@ -63,8 +62,7 @@ public record JPlagComparison(Submission firstSubmission, Submission secondSubmi
     }
 
     /**
-     * @return Similarity in percent for the second submission (what percent of the second submission is similar to the
-     * first).
+     * @return Similarity of the second submission in interval [0, 1]. O means no similarity, 1 means maximum similarity.
      */
     public final double similarityOfSecond() {
         int divisor = secondSubmission.getSimilarityDivisor(true);
@@ -77,6 +75,6 @@ public record JPlagComparison(Submission firstSubmission, Submission secondSubmi
     }
 
     private double similarity(int divisor) {
-        return (divisor == 0 ? 0.0 : (getNumberOfMatchedTokens() * 100 / (double) divisor));
+        return (divisor == 0 ? 0.0 : (getNumberOfMatchedTokens() / (double) divisor));
     }
 }

+ 8 - 9
core/src/main/java/de/jplag/JPlagResult.java

@@ -26,7 +26,7 @@ public class JPlagResult {
     private final int SIMILARITY_DISTRIBUTION_SIZE = 10;
 
     public JPlagResult(List<JPlagComparison> comparisons, SubmissionSet submissions, long durationInMillis, JPlagOptions options) {
-        // sort comparisons by percentage (descending)
+        // sort by similarity (descending)
         this.comparisons = comparisons.stream().sorted((first, second) -> Double.compare(second.similarity(), first.similarity())).toList();
         this.submissions = submissions;
         this.durationInMillis = durationInMillis;
@@ -36,7 +36,7 @@ public class JPlagResult {
 
     /**
      * Drops elements from the comparison list to free memory. Note, that this affects the similarity distribution and is
-     * only meant to be used if you don't need the information about comparisons with lower match percentage anymore.
+     * only meant to be used if you don't need the information about comparisons with lower match similarity anymore.
      * @param limit the number of comparisons to keep in the list
      */
     public void dropComparisons(int limit) {
@@ -48,17 +48,17 @@ public class JPlagResult {
     }
 
     /**
-     * @return a list of all comparisons sorted by percentage (descending)
+     * @return a list of all comparisons sorted by similarity (descending)
      */
     public List<JPlagComparison> getAllComparisons() {
         return comparisons;
     }
 
     /**
-     * Returns the first n comparisons (sorted by percentage, descending), limited by the specified parameter.
+     * Returns the first n comparisons (sorted by similarity, descending), limited by the specified parameter.
      * @param numberOfComparisons specifies the number of requested comparisons. If set to -1, all comparisons will be
      * returned.
-     * @return a list of comparisons sorted descending by percentage.
+     * @return a list of comparisons sorted descending by similarity.
      */
     public List<JPlagComparison> getComparisons(int numberOfComparisons) {
         if (numberOfComparisons == JPlagOptions.SHOW_ALL_COMPARISONS) {
@@ -136,11 +136,10 @@ public class JPlagResult {
 
     private int[] calculateDistributionFor(List<JPlagComparison> comparisons, ToDoubleFunction<JPlagComparison> similarityExtractor) {
         int[] similarityDistribution = new int[SIMILARITY_DISTRIBUTION_SIZE];
-        int similarityDistributionBucketSize = 100 / SIMILARITY_DISTRIBUTION_SIZE;
         for (JPlagComparison comparison : comparisons) {
-            double similarity = similarityExtractor.applyAsDouble(comparison); // extract similarity in percent: 0f <= similarity <= 100f
-            int index = (int) (similarity / similarityDistributionBucketSize); // divide similarity by bucket size to find index of correct bucket.
-            index = Math.min(index, SIMILARITY_DISTRIBUTION_SIZE - 1);// index is out of bounds when similarity is 100%. decrease by one to count
+            double similarity = similarityExtractor.applyAsDouble(comparison); // extract similarity: 0.0 <= similarity <= 1.0
+            int index = (int) (similarity * SIMILARITY_DISTRIBUTION_SIZE); // divide similarity by bucket size to find index of correct bucket.
+            index = Math.min(index, SIMILARITY_DISTRIBUTION_SIZE - 1);// index is out of bounds when similarity is 1.0. decrease by one to count
                                                                       // towards the highest value bucket
             similarityDistribution[SIMILARITY_DISTRIBUTION_SIZE - 1 - index]++; // count comparison towards its determined bucket. bucket order is
             // reversed, so that the highest value bucket has the lowest index

+ 4 - 4
core/src/main/java/de/jplag/options/JPlagOptions.java

@@ -36,7 +36,7 @@ import de.jplag.clustering.ClusteringOptions;
  * @param similarityMetric The similarity metric determines how the minimum similarity threshold required for a
  * comparison (of two submissions) is calculated. This affects which comparisons are stored and thus make it into the
  * result object.
- * @param similarityThreshold Percentage value (must be between 0 and 100). Comparisons (of submissions pairs) with a
+ * @param similarityThreshold Similarity value (must be between 0 and 1). Comparisons (of submissions pairs) with a
  * similarity below this threshold will be ignored. The default value of 0 allows all matches to be stored. This affects
  * which comparisons are stored and thus make it into the result object. See also {@link #similarityMetric()}.
  * @param maximumNumberOfComparisons The maximum number of comparisons that will be shown in the generated report. If
@@ -222,9 +222,9 @@ public record JPlagOptions(Language language, Integer minimumTokenMatch, List<St
     }
 
     private static double normalizeSimilarityThreshold(double similarityThreshold) {
-        if (similarityThreshold > 100) {
-            logger.warn("Maximum threshold of 100 used instead of {}", similarityThreshold);
-            return 100;
+        if (similarityThreshold > 1) {
+            logger.warn("Maximum threshold of 1 used instead of {}", similarityThreshold);
+            return 1;
         } else if (similarityThreshold < 0) {
             logger.warn("Minimum threshold of 0 used instead of {}", similarityThreshold);
             return 0;

+ 2 - 2
core/src/main/java/de/jplag/options/SimilarityMetric.java

@@ -12,8 +12,8 @@ public enum SimilarityMetric implements ToDoubleFunction<JPlagComparison> {
 
     private final ToDoubleFunction<JPlagComparison> similarityFunction;
 
-    SimilarityMetric(ToDoubleFunction<JPlagComparison> determinePercentage) {
-        this.similarityFunction = determinePercentage;
+    SimilarityMetric(ToDoubleFunction<JPlagComparison> similarityFunction) {
+        this.similarityFunction = similarityFunction;
     }
 
     public boolean isAboveThreshold(JPlagComparison comparison, double similarityThreshold) {

+ 2 - 2
core/src/main/java/de/jplag/reporting/reportobject/model/ComparisonReport.java

@@ -8,10 +8,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
  * ReportViewer DTO for the comparison of two submissions.
  * @param firstSubmissionId id of the first submission
  * @param secondSubmissionId id of the second submission
- * @param matchPercentage similarity in percent. between 0f and 100f.
+ * @param similarity average similarity. between 0.0 and 1.0.
  * @param matches the list of matches found in the comparison of the two submissions
  */
 public record ComparisonReport(@JsonProperty("id1") String firstSubmissionId, @JsonProperty("id2") String secondSubmissionId,
-        @JsonProperty("similarity") double matchPercentage, @JsonProperty("matches") List<Match> matches) {
+        @JsonProperty("similarity") double similarity, @JsonProperty("matches") List<Match> matches) {
 
 }

+ 1 - 1
core/src/main/java/de/jplag/reporting/reportobject/model/TopComparison.java

@@ -3,5 +3,5 @@ package de.jplag.reporting.reportobject.model;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
 public record TopComparison(@JsonProperty("first_submission") String firstSubmission, @JsonProperty("second_submission") String secondSubmission,
-        @JsonProperty("match_percentage") double matchPercentage) {
+        @JsonProperty("similarity") double similarity) {
 }

+ 1 - 1
core/src/main/resources/de/jplag/messages.properties

@@ -9,7 +9,7 @@ CommandLineArgument.RootDirectory=Root-directory with submissions to check for p
 CommandLineArgument.PlagiarismDirectory=Root-directory with submissions to check for plagiarism
 CommandLineArgument.PriorDirectory=Root-directory with prior submissions to compare against
 CommandLineArgument.ShownComparisons=The maximum number of comparisons that will be shown in the generated report, if set to -1 all comparisons will be shown
-CommandLineArgument.SimilarityThreshold=Comparison similarity threshold [0-100]: All comparisons above this threshold will be saved
+CommandLineArgument.SimilarityThreshold=Comparison similarity threshold [0.0-1.0]: All comparisons above this threshold will be saved
 CommandLineArgument.Subdirectory=Look in directories <root-dir>/*/<dir> for programs
 CommandLineArgument.Verbosity=Verbosity of the logging
 CommandLineArgument.ClusterDisable=Skips the clustering

+ 1 - 1
core/src/test/java/de/jplag/BaseCodeTest.java

@@ -42,7 +42,7 @@ public class BaseCodeTest extends TestBase {
         assertEquals(1, result.getAllComparisons().size());
         assertEquals(1, result.getAllComparisons().get(0).matches().size());
         assertEquals(1, result.getSimilarityDistribution()[1]);
-        assertEquals(85, result.getAllComparisons().get(0).similarity(), DELTA);
+        assertEquals(0.85, result.getAllComparisons().get(0).similarity(), DELTA);
     }
 
     @Test

+ 1 - 1
core/src/test/java/de/jplag/NewJavaFeaturesTest.java

@@ -8,7 +8,7 @@ import de.jplag.exceptions.ExitException;
 
 public class NewJavaFeaturesTest extends TestBase {
     private static final int EXPECTED_MATCHES = 6; // might change if you add files to the submissions
-    private static final double EXPECTED_SIMILARITY = 96.0; // might change if you add files to the submissions
+    private static final double EXPECTED_SIMILARITY = 0.96; // might change if you add files to the submissions
 
     private static final String EXCLUSION_FILE_NAME = "blacklist.txt";
     private static final String ROOT_DIRECTORY = "NewJavaFeatures";

+ 10 - 10
core/src/test/java/de/jplag/ParallelComparisonTest.java

@@ -25,7 +25,7 @@ public class ParallelComparisonTest extends TestBase {
         assertEquals(1, result.getAllComparisons().size());
         assertEquals(1, result.getAllComparisons().get(0).matches().size());
         assertEquals(1, result.getSimilarityDistribution()[3]);
-        assertEquals(62.07, result.getAllComparisons().get(0).similarity(), DELTA);
+        assertEquals(0.6207, result.getAllComparisons().get(0).similarity(), DELTA);
     }
 
     /**
@@ -40,7 +40,7 @@ public class ParallelComparisonTest extends TestBase {
         assertEquals(1, result.getAllComparisons().size());
         assertEquals(2, result.getAllComparisons().get(0).matches().size());
         assertArrayEquals(expectedDistribution, result.getSimilarityDistribution());
-        assertEquals(96.55, result.getAllComparisons().get(0).similarity(), DELTA);
+        assertEquals(0.9655, result.getAllComparisons().get(0).similarity(), DELTA);
     }
 
     /**
@@ -74,17 +74,17 @@ public class ParallelComparisonTest extends TestBase {
                 .forEach(comparison -> assertEquals(0, comparison.similarity(), DELTA));
 
         // Hard coded assertions on selected comparisons
-        assertEquals(24.6, getSelectedPercent(result, "A", "B"), DELTA);
-        assertEquals(99.7, getSelectedPercent(result, "A", "C"), DELTA);
-        assertEquals(77.9, getSelectedPercent(result, "A", "D"), DELTA);
-        assertEquals(24.6, getSelectedPercent(result, "B", "C"), DELTA);
-        assertEquals(28.3, getSelectedPercent(result, "B", "D"), DELTA);
-        assertEquals(77.9, getSelectedPercent(result, "C", "D"), DELTA);
+        assertEquals(0.246, getSelectedPercent(result, "A", "B"), DELTA);
+        assertEquals(0.997, getSelectedPercent(result, "A", "C"), DELTA);
+        assertEquals(0.779, getSelectedPercent(result, "A", "D"), DELTA);
+        assertEquals(0.246, getSelectedPercent(result, "B", "C"), DELTA);
+        assertEquals(0.283, getSelectedPercent(result, "B", "D"), DELTA);
+        assertEquals(0.779, getSelectedPercent(result, "C", "D"), DELTA);
 
         // More detailed assertions for the plagiarism in A-D
         var biggestMatch = getSelectedComparison(result, "A", "D");
-        assertEquals(96.4, biggestMatch.get().maximalSimilarity(), DELTA);
-        assertEquals(65.3, biggestMatch.get().minimalSimilarity(), DELTA);
+        assertEquals(0.964, biggestMatch.get().maximalSimilarity(), DELTA);
+        assertEquals(0.653, biggestMatch.get().minimalSimilarity(), DELTA);
         assertEquals(12, biggestMatch.get().matches().size());
     }
 

+ 1 - 1
core/src/test/java/de/jplag/TestBase.java

@@ -14,7 +14,7 @@ import de.jplag.options.Verbosity;
 public abstract class TestBase {
 
     protected static final String BASE_PATH = Path.of("src", "test", "resources", "de", "jplag", "samples").toString();
-    protected static final double DELTA = 0.1;
+    protected static final double DELTA = 0.001;
 
     protected String getBasePath() {
         return BASE_PATH;

+ 1 - 1
core/src/test/java/de/jplag/special/VolumeTest.java

@@ -36,7 +36,7 @@ public class VolumeTest extends TestBase {
 
     /**
      * This test requires a folder "data" with submissions and a file named "matches_avg.csv" inside the volume folder.
-     * Accepts a derivation of 0.1% in the matching percentage
+     * Accepts a derivation of {@link #DELTA} in the matching similarity
      */
     @Test
     @Disabled

Dosya farkı çok büyük olduğundan ihmal edildi
+ 317 - 317
endtoend-testing/src/test/resources/results/java/sortAlgo.json


+ 6 - 6
report-viewer/src/components/ComparisonsTable.vue

@@ -15,7 +15,7 @@
       :key="
         comparison.firstSubmissionId +
         comparison.secondSubmissionId +
-        comparison.matchPercentage
+        comparison.similarity
       "
       class="selectable"
     >
@@ -81,7 +81,7 @@
           )
         "
       >
-        {{ formattedMatchPercentage(comparison.matchPercentage) }}
+        {{ formattedMatchPercentage(comparison.similarity) }}
       </td>
       <td>
         <img
@@ -144,7 +144,7 @@ export default defineComponent({
   },
   setup(props) {
     const store = useStore();
-    let formattedMatchPercentage = (num: number) => num.toFixed(2);
+    let formattedMatchPercentage = (num: number) => (num * 100).toFixed(2);
     const dialog: Ref<Array<boolean>> = ref([]);
     props.topComparisons.forEach(() => dialog.value.push(false));
     const displayName = (submissionId: string) =>
@@ -154,7 +154,7 @@ export default defineComponent({
       dialog.value[index] = true;
     };
 
-    const navigateToComparisonView = (firstId: string, secondId: string) => {
+    const navigateToComparisonView = (firstId : string, secondId: string) => {
       if (!store.state.single) {
         router.push({
           name: "ComparisonView",
@@ -185,7 +185,7 @@ export default defineComponent({
         ) {
           matches.push({
             matchedWith: comparison.secondSubmissionId,
-            percentage: comparison.matchPercentage,
+            percentage: comparison.similarity,
           });
         } else if (
           comparison.secondSubmissionId.includes(id) &&
@@ -193,7 +193,7 @@ export default defineComponent({
         ) {
           matches.push({
             matchedWith: comparison.firstSubmissionId,
-            percentage: comparison.matchPercentage,
+            percentage: comparison.similarity,
           });
         }
       });

+ 5 - 5
report-viewer/src/model/Comparison.ts

@@ -8,16 +8,16 @@ import { MatchInSingleFile } from "./MatchInSingleFile";
 export class Comparison {
   private readonly _firstSubmissionId: string;
   private readonly _secondSubmissionId: string;
-  private readonly _match_percentage: number;
+  private readonly _similarity: number;
 
   constructor(
     firstSubmissionId: string,
     secondSubmissionId: string,
-    match_percentage: number
+    similarity: number
   ) {
     this._firstSubmissionId = firstSubmissionId;
     this._secondSubmissionId = secondSubmissionId;
-    this._match_percentage = match_percentage;
+    this._similarity = similarity;
     this._filesOfFirstSubmission = new Map();
     this._filesOfSecondSubmission = new Map();
     this._colors = [];
@@ -94,7 +94,7 @@ export class Comparison {
     return this._secondSubmissionId;
   }
 
-  get match_percentage(): number {
-    return this._match_percentage;
+  get similarity(): number {
+    return this._similarity;
   }
 }

+ 1 - 1
report-viewer/src/model/ComparisonListElement.ts

@@ -5,5 +5,5 @@
 export type ComparisonListElement = {
     firstSubmissionId: string,
     secondSubmissionId: string,
-    matchPercentage: number
+    similarity: number
 }

+ 1 - 1
report-viewer/src/model/factories/OverviewFactory.ts

@@ -27,7 +27,7 @@ export class OverviewFactory {
           const comparison: ComparisonListElement = {
             firstSubmissionId: jsonComparison.first_submission as string,
             secondSubmissionId: jsonComparison.second_submission as string,
-            matchPercentage: jsonComparison.match_percentage as number,
+            similarity: jsonComparison.similarity as number,
           };
           comparisons.push(comparison);
         }

+ 1 - 1
report-viewer/src/views/ComparisonView.vue

@@ -31,7 +31,7 @@
         :value="store.getters.submissionDisplayName(secondId)"
         label="Submission 2"
       />
-      <TextInformation :value="comparison.match_percentage" label="Match %" />
+      <TextInformation :value="(comparison.similarity * 100).toFixed(2)" label="Match %" />
       <MatchTable
         :id1="firstId"
         :id2="secondId"

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor