.claude/skills/similarity/SKILL.md
Use when working with component similarity calculations - comparing MPNs, finding equivalent parts, implementing new similarity calculators, or understanding how component matching works.
npx skillsauth add Cantara/lib-electronic-components similarityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides guidance for working with component similarity calculators in the lib-electronic-components library.
For metadata-driven similarity architecture, see /similarity-metadata:
Similarity calculators determine how similar two electronic components are based on their MPNs (Manufacturer Part Numbers). They return a value between 0.0 (completely different) and 1.0 (identical or equivalent).
public interface SimilarityCalculator {
double calculateSimilarity(String normalizedMpn1, String normalizedMpn2);
}
Used for generic calculators that don't need component type context.
public interface ComponentSimilarityCalculator {
boolean isApplicable(ComponentType type);
double calculateSimilarity(String mpn1, String mpn2, PatternRegistry registry);
}
Used for component-specific calculators that need to check applicability.
private static final double HIGH_SIMILARITY = 0.9; // Equivalent/interchangeable
private static final double MEDIUM_SIMILARITY = 0.7; // Similar, may work as substitute
private static final double LOW_SIMILARITY = 0.3; // Same category but different specs
| Calculator | Interface | Component Types | Key Features |
|------------|-----------|-----------------|--------------|
| ResistorSimilarityCalculator | Component | RESISTOR, RESISTOR_* | Value, package, tolerance |
| CapacitorSimilarityCalculator | Component | CAPACITOR, CAPACITOR_* | Value, voltage, dielectric |
| TransistorSimilarityCalculator | Component | TRANSISTOR, TRANSISTOR_* | NPN/PNP polarity, equivalent groups |
| DiodeSimilarityCalculator | Component | DIODE, DIODE_* | Signal/rectifier/zener types |
| MosfetSimilarityCalculator | Component | MOSFET, MOSFET_* | N/P channel, equivalent groups |
| OpAmpSimilarityCalculator | Component | OPAMP, OPAMP_* | Single/dual/quad, equivalent families |
| VoltageRegulatorSimilarityCalculator | Component | VOLTAGE_REGULATOR* | Fixed (78xx) vs adjustable (LM317) |
| LogicICSimilarityCalculator | Component | LOGIC_IC, IC | 74xx/CD4000 series, function groups |
| LEDSimilarityCalculator | Component | LED, LED_* | Color, bins, families |
| MemorySimilarityCalculator | Component | MEMORY, MEMORY_* | I2C/SPI EEPROM, Flash equivalents |
| SensorSimilarityCalculator | Component | SENSOR, TEMPERATURE_SENSOR, ACCELEROMETER | Sensor families, package variants |
| ConnectorSimilarityCalculator | Component | CONNECTOR, CONNECTOR_* | Pin count, pitch, family |
| MicrocontrollerSimilarityCalculator | Component | MICROCONTROLLER* | Series, package, manufacturer |
| MCUSimilarityCalculator | Simple | (generic) | Family, series, features |
| PassiveComponentCalculator | Simple | (generic) | Value, size code, tolerance |
| LevenshteinCalculator | Simple | (generic) | String edit distance |
| DefaultSimilarityCalculator | Simple | (generic) | Prefix, numeric, suffix weights |
public class NewComponentSimilarityCalculator implements ComponentSimilarityCalculator {
private static final double HIGH_SIMILARITY = 0.9;
private static final double MEDIUM_SIMILARITY = 0.7;
private static final double LOW_SIMILARITY = 0.3;
@Override
public boolean isApplicable(ComponentType type) {
if (type == null) return false;
return type == ComponentType.NEW_COMPONENT ||
type.name().startsWith("NEW_COMPONENT_");
}
@Override
public double calculateSimilarity(String mpn1, String mpn2, PatternRegistry registry) {
if (mpn1 == null || mpn2 == null) return 0.0;
// Check if both are the component type we handle
if (!isComponentType(mpn1) || !isComponentType(mpn2)) {
return 0.0;
}
// Compare components
// ...
return similarity;
}
}
sim(A,B) == sim(B,A)private static final Map<String, Set<String>> EQUIVALENT_GROUPS = new HashMap<>();
static {
EQUIVALENT_GROUPS.put("2N2222", Set.of("2N2222", "2N2222A", "PN2222", "PN2222A"));
}
private boolean areEquivalent(String mpn1, String mpn2) {
for (Set<String> group : EQUIVALENT_GROUPS.values()) {
if (group.contains(mpn1) && group.contains(mpn2)) {
return true;
}
}
return false;
}
private String extractBasePart(String mpn) {
// Remove common package suffixes
return mpn.replaceAll("(?:CT|T|N|P|DG|PW|DR)$", "");
}
private boolean areSamePolarity(String mpn1, String mpn2) {
boolean isNPN1 = NPN_PATTERNS.stream().anyMatch(mpn1::matches);
boolean isNPN2 = NPN_PATTERNS.stream().anyMatch(mpn2::matches);
return isNPN1 == isNPN2;
}
@Nested
@DisplayName("isApplicable tests")
class IsApplicableTests { /* ... */ }
@Nested
@DisplayName("Equivalent groups tests")
class EquivalentGroupTests { /* ... */ }
@Nested
@DisplayName("Edge cases and null handling")
class EdgeCaseTests { /* ... */ }
@Nested
@DisplayName("Symmetry and property tests")
class PropertyTests { /* ... */ }
# All similarity calculator tests
mvn test -Dtest="*SimilarityCalculatorTest,PassiveComponentCalculatorTest"
# Specific calculator
mvn test -Dtest=TransistorSimilarityCalculatorTest
/similarity-resistor - Resistor similarity details/similarity-transistor - Transistor equivalent groups and polarity/similarity-mosfet - MOSFET N/P channel comparison/similarity-opamp - Op-amp families and equivalents/similarity-memory - Memory IC equivalents (I2C/SPI EEPROM, Flash)/similarity-sensor - Sensor family comparison/similarity-led - LED bins and color temperature/similarity-regulator - Voltage regulator comparison (78xx, LM317)/similarity-logic - Logic IC function groups (74xx, CD4000)The similarity system now uses a metadata-driven architecture for configurable, type-specific similarity rules.
Conversion Status: 12 of 17 calculators converted (71% complete)
| Calculator | Status | PR | Conversion Date | |-----------|--------|-----|-----------------| | ResistorSimilarityCalculator | ✅ Converted | - | Jan 2026 | | CapacitorSimilarityCalculator | ✅ Converted | - | Jan 2026 | | TransistorSimilarityCalculator | ✅ Converted | - | Jan 2026 | | DiodeSimilarityCalculator | ✅ Converted | - | Jan 2026 | | MosfetSimilarityCalculator | ✅ Converted | - | Jan 2026 | | VoltageRegulatorSimilarityCalculator | ✅ Converted | - | Jan 2026 | | OpAmpSimilarityCalculator | ✅ Converted | #116 | Jan 2026 | | MemorySimilarityCalculator | ✅ Converted | #117 | Jan 2026 | | LEDSimilarityCalculator | ✅ Converted | #118 | Jan 2026 | | ConnectorSimilarityCalculator | ✅ Converted | (pre-existing) | Jan 2026 | | LogicICSimilarityCalculator | ✅ Converted | #119 | Jan 2026 | | SensorSimilarityCalculator | ✅ Converted | #120 | Jan 2026 | | MicrocontrollerSimilarityCalculator | ⏳ Legacy | - | - | | MCUSimilarityCalculator | ⏳ Legacy | - | - | | PassiveComponentCalculator | ⏳ Legacy | - | - | | LevenshteinCalculator | ⏳ Legacy | - | - | | DefaultSimilarityCalculator | ⏳ Legacy | - | - |
| Class | Purpose |
|-------|---------|
| ComponentTypeMetadata | Defines specs, importance levels, tolerance rules per component type |
| ComponentTypeMetadataRegistry | Singleton registry mapping ComponentType → metadata |
| SpecImportance | Enum: CRITICAL (1.0), HIGH (0.7), MEDIUM (0.4), LOW (0.2), OPTIONAL (0.0) |
| ToleranceRule | Interface for comparing spec values (ExactMatch, Percentage, MinRequired, MaxAllowed, Range) |
| SimilarityProfile | Context-aware profiles (DESIGN_PHASE, REPLACEMENT, COST_OPTIMIZATION, PERFORMANCE_UPGRADE, EMERGENCY_SOURCING) |
ComponentTypeMetadataRegistry registry = ComponentTypeMetadataRegistry.getInstance();
// Get metadata for a component type
Optional<ComponentTypeMetadata> metadata = registry.getMetadata(ComponentType.RESISTOR);
// Query specs
if (metadata.isPresent()) {
ComponentTypeMetadata meta = metadata.get();
// Check if spec is critical
boolean critical = meta.isCritical("resistance"); // true
// Get tolerance rule for a spec
SpecConfig config = meta.getSpecConfig("resistance");
if (config != null) {
ToleranceRule rule = config.getToleranceRule();
SpecImportance importance = config.getImportance();
}
// Get all configured specs
Set<String> allSpecs = meta.getAllSpecs();
}
RESISTOR, CAPACITOR, MOSFET, TRANSISTOR, DIODE, OPAMP, MICROCONTROLLER, MEMORY, LED, CONNECTOR
Each type has:
Adjust importance multipliers based on use case:
| Profile | Threshold | CRITICAL | HIGH | MEDIUM | LOW | Use Case | |---------|-----------|----------|------|--------|-----|----------| | DESIGN_PHASE | 0.85 | 1.0 | 0.9 | 0.7 | 0.4 | Exact match for new designs | | REPLACEMENT | 0.75 | 1.0 | 0.7 | 0.4 | 0.2 | Default: Direct replacement | | COST_OPTIMIZATION | 0.60 | 1.0 | 0.4 | 0.2 | 0.0 | Maintain critical specs only | | EMERGENCY_SOURCING | 0.50 | 0.8 | 0.4 | 0.2 | 0.0 | Urgent, relaxed requirements |
// Check if similarity meets threshold for a profile
SimilarityProfile profile = SimilarityProfile.REPLACEMENT;
double similarity = 0.78;
boolean passes = profile.meetsThreshold(similarity); // true
// Get effective weight for a spec
double effectiveWeight = profile.getEffectiveWeight(SpecImportance.HIGH); // 0.7 × 0.7 = 0.49
Calculators converted to metadata-driven approach follow this pattern:
@Override
public double calculateSimilarity(String mpn1, String mpn2, PatternRegistry registry) {
if (mpn1 == null || mpn2 == null) return 0.0;
// Try metadata-driven approach first
Optional<ComponentTypeMetadata> metadataOpt = metadataRegistry.getMetadata(ComponentType.OPAMP);
if (metadataOpt.isPresent()) {
logger.trace("Using metadata-driven similarity calculation");
return calculateMetadataDrivenSimilarity(mpn1, mpn2, metadataOpt.get());
}
// Fallback to legacy pattern-based approach
logger.trace("No metadata found, using legacy approach");
return calculateLegacySimilarity(mpn1, mpn2);
}
private double calculateMetadataDrivenSimilarity(String mpn1, String mpn2, ComponentTypeMetadata metadata) {
SimilarityProfile profile = metadata.getDefaultProfile();
// Extract specs from MPNs
String config1 = extractConfiguration(mpn1); // e.g., "dual", "quad"
String config2 = extractConfiguration(mpn2);
// ... extract other specs
// Short-circuit check for CRITICAL incompatibility
if (!config1.isEmpty() && !config2.isEmpty() && !config1.equals(config2)) {
return LOW_SIMILARITY;
}
double totalScore = 0.0;
double maxPossibleScore = 0.0;
// Compare each spec with weighted scoring
ComponentTypeMetadata.SpecConfig configSpec = metadata.getSpecConfig("configuration");
if (configSpec != null && !config1.isEmpty() && !config2.isEmpty()) {
ToleranceRule rule = configSpec.getToleranceRule();
SpecValue<String> orig = new SpecValue<>(config1, SpecUnit.NONE);
SpecValue<String> cand = new SpecValue<>(config2, SpecUnit.NONE);
double specScore = rule.compare(orig, cand);
double specWeight = profile.getEffectiveWeight(configSpec.getImportance());
totalScore += specScore * specWeight;
maxPossibleScore += specWeight;
}
// Repeat for other specs (family, package, etc.)
// ...
double similarity = maxPossibleScore > 0 ? totalScore / maxPossibleScore : 0.0;
// Apply boosts for equivalent groups
if (areEquivalentParts(mpn1, mpn2)) {
similarity = Math.max(similarity, HIGH_SIMILARITY);
}
return similarity;
}
Key Features of Converted Calculators:
totalScore / maxPossibleScore formulagetEffectiveWeight() for context-aware weights| Calculator | Critical Specs | High Importance | Medium Importance | Low Importance | |-----------|----------------|-----------------|-------------------|----------------| | OpAmp | configuration | family | package | - | | Memory | memoryType, capacity | interface | - | package | | LED | color | family, brightness | - | package | | Connector | pinCount, pitch | family | mountingType | - | | LogicIC | function | series, technology | - | package | | Sensor | sensorType | family | interface | package |
For converting existing calculators:
SimilarityProfile, ToleranceRule, SpecUnit, SpecValuecalculateSimilarity() to check for metadata firstcalculateMetadataDrivenSimilarity() method>= HIGH_SIMILARITY)For new calculators:
ComponentTypeMetadataRegistrySpecValue wrapper for type-safe comparisonSimilarityProfileSee CLAUDE.md § "Metadata-Driven Similarity Framework" for complete architecture details.
^[A-Za-z]+ won't matchSpecValue Instantiation:
// NO static factory - use constructor
SpecValue<Double> v = new SpecValue<>(100.0, SpecUnit.FARAD); // ✓
SpecValue<Double> v = SpecValue.of(100.0); // ✗ Does not exist
API Return Types:
// Registry returns Optional, metadata methods return direct values
Optional<ComponentTypeMetadata> meta = registry.getMetadata(type); // Optional
SpecConfig config = metadata.getSpecConfig("resistance"); // Can be null
boolean critical = metadata.isCritical("resistance"); // false if not found
Singleton Side Effects:
Profile Multiplier Values:
Builder Validation:
ComponentTypeMetadata.builder(null).build(); // IllegalArgumentException
ComponentTypeMetadata.builder(ComponentType.IC).build(); // IllegalStateException (no specs)
<!-- Add new learnings above this line -->
/similarity-calculator-architecture - Calculator registration, ordering, and the OpAmp IC interception bug/metadata-driven-similarity-conversion - Converting calculators to metadata-driven approach/component-spec-extraction - How to extract specs from MPNs for comparison/equivalent-group-identification - Hardcoded equivalent groups across calculatorsdata-ai
Cost-effective task delegation strategy using Haiku model for straightforward work. Use when planning how to approach simple, pattern-following tasks to minimize costs.
testing
Use when working with transistor similarity calculations - comparing BJT MPNs, understanding NPN/PNP polarity matching, equivalent groups like 2N2222/PN2222, or transistor-specific similarity logic.
testing
Use when working with sensor similarity calculations - comparing temperature/accelerometer/humidity sensor MPNs, understanding sensor families, equivalent parts, or sensor-specific similarity logic.
testing
Use when working with resistor similarity calculations - comparing resistor MPNs, understanding value/tolerance/package matching, or implementing resistor-specific similarity logic.