java-topology/defects/prometheus/unit/PrometheusTest.java

176 lines
6.5 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import java.util.*;
/**
* Unit test for prometheus-0001: dependencyMap.dependencies() O(R²) defect.
*
* The defect: AnalyseRules() calls dependencies(r) for each of R rules.
* dependencies() iterates all entries in the dependency map (R entries) and for
* each calls slices.Contains(dependents, r) — an O(D) linear scan of the
* dependents slice. Total cost: O(R × R × D) ≈ O(R²) for sparse graphs.
*
* The fix: build an inverse map (rule → rules it depends on) once in O(R×D),
* then each dependencies() call is a single O(1) map lookup.
*
* This Java simulation models the two strategies, counts operations, and
* confirms the asymptotic ratio grows with the number of rules R.
*/
public class PrometheusTest {
// Simulate a Rule as a simple integer ID.
static class Rule {
final int id;
Rule(int id) { this.id = id; }
@Override public boolean equals(Object o) {
return o instanceof Rule && ((Rule)o).id == id;
}
@Override public int hashCode() { return Integer.hashCode(id); }
@Override public String toString() { return "Rule(" + id + ")"; }
}
// dependencyMap: rule -> list of rules that depend on it (its "dependents")
static Map<Rule, List<Rule>> buildForwardMap(List<Rule> rules, int branchFactor) {
Map<Rule, List<Rule>> fwd = new IdentityHashMap<>();
for (int i = 0; i < rules.size(); i++) {
Rule rule = rules.get(i);
List<Rule> dependents = new ArrayList<>();
// Simulate: each rule is a dependent of the previous `branchFactor` rules
for (int b = 1; b <= branchFactor && i + b < rules.size(); b++) {
dependents.add(rules.get(i + b));
}
if (!dependents.isEmpty()) {
fwd.put(rule, dependents);
}
}
return fwd;
}
/**
* DEFECTIVE dependencies(): scan all forward-map entries for r in dependents.
* Returns total operation count (each slices.Contains step = 1 op).
*/
static long defectiveDependencies(Rule r, Map<Rule, List<Rule>> fwd) {
long ops = 0;
List<Rule> result = new ArrayList<>();
for (Map.Entry<Rule, List<Rule>> e : fwd.entrySet()) {
List<Rule> dependents = e.getValue();
for (Rule dep : dependents) { // simulates slices.Contains
ops++;
if (dep.equals(r)) {
result.add(e.getKey());
break;
}
}
}
return ops;
}
/**
* FIXED dependencies(): O(1) map lookup into pre-built inverse map.
*/
static long fixedDependencies(Rule r, Map<Rule, List<Rule>> inv) {
long ops = 1; // single map lookup
inv.get(r); // O(1)
return ops;
}
/** Build inverse map: O(R*D). */
static Map<Rule, List<Rule>> buildInverseMap(Map<Rule, List<Rule>> fwd) {
Map<Rule, List<Rule>> inv = new IdentityHashMap<>();
for (Map.Entry<Rule, List<Rule>> e : fwd.entrySet()) {
for (Rule dep : e.getValue()) {
inv.computeIfAbsent(dep, k -> new ArrayList<>()).add(e.getKey());
}
}
return inv;
}
/** Simulate AnalyseRules over R rules. */
static long runDefective(List<Rule> rules, Map<Rule, List<Rule>> fwd) {
long totalOps = 0;
for (Rule r : rules) {
totalOps += defectiveDependencies(r, fwd);
}
return totalOps;
}
static long runFixed(List<Rule> rules, Map<Rule, List<Rule>> fwd) {
long totalOps = 0;
// Build inverse map once
Map<Rule, List<Rule>> inv = buildInverseMap(fwd);
// O(R*D) build cost counted separately
for (Rule r : rules) {
totalOps += fixedDependencies(r, inv);
}
return totalOps;
}
public static void main(String[] args) {
System.out.println("prometheus-0001: dependencyMap.dependencies() O(R²) defect");
System.out.println("===========================================================");
int[] ruleCounts = {10, 50, 100, 200, 500};
int branchFactor = 3; // each rule depends on 3 next rules
boolean allPass = true;
for (int R : ruleCounts) {
List<Rule> rules = new ArrayList<>(R);
for (int i = 0; i < R; i++) rules.add(new Rule(i));
Map<Rule, List<Rule>> fwd = buildForwardMap(rules, branchFactor);
long defOps = runDefective(rules, fwd);
long fixOps = runFixed(rules, fwd);
double ratio = (double) defOps / Math.max(fixOps, 1);
System.out.printf("R=%4d branchFactor=%d defect_ops=%,8d fixed_ops=%,6d ratio=%.1fx%n",
R, branchFactor, defOps, fixOps, ratio);
if (ratio < 3.0) {
System.err.println(" FAIL: expected ratio >= 3.0 at R=" + R);
allPass = false;
}
}
// Correctness: fixed produces same dependencies as defective
{
int R = 20;
List<Rule> rules = new ArrayList<>(R);
for (int i = 0; i < R; i++) rules.add(new Rule(i));
Map<Rule, List<Rule>> fwd = buildForwardMap(rules, 2);
Map<Rule, List<Rule>> inv = buildInverseMap(fwd);
boolean correct = true;
for (Rule r : rules) {
// Collect via defective
List<Rule> defDeps = new ArrayList<>();
for (Map.Entry<Rule, List<Rule>> e : fwd.entrySet()) {
if (e.getValue().contains(r)) defDeps.add(e.getKey());
}
// Collect via fixed
List<Rule> fixDeps = inv.getOrDefault(r, Collections.emptyList());
Set<Integer> defIds = new HashSet<>();
for (Rule d : defDeps) defIds.add(d.id);
Set<Integer> fixIds = new HashSet<>();
for (Rule d : fixDeps) fixIds.add(d.id);
if (!defIds.equals(fixIds)) {
System.err.println(" FAIL: mismatch for " + r + " defective=" + defIds + " fixed=" + fixIds);
correct = false;
allPass = false;
}
}
if (correct) {
System.out.println("Correctness check: PASS (dependency sets match)");
}
}
if (allPass) {
System.out.println("ALL TESTS PASS");
System.exit(0);
} else {
System.out.println("SOME TESTS FAILED");
System.exit(1);
}
}
}