Lib-electronic-components similarity

Use when working with component similarity calculations - comparing MPNs, finding equivalent parts, implementing new similarity calculators, or understanding how component matching works.

install
source · Clone the upstream repo
git clone https://github.com/Cantara/lib-electronic-components
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Cantara/lib-electronic-components "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/similarity" ~/.claude/skills/cantara-lib-electronic-components-similarity && rm -rf "$T"
manifest: .claude/skills/similarity/SKILL.md
source content

Component Similarity Calculator Skill

This skill provides guidance for working with component similarity calculators in the lib-electronic-components library.


For metadata-driven similarity architecture, see

/similarity-metadata
:

  • SpecImportance levels (CRITICAL, HIGH, MEDIUM, LOW, OPTIONAL)
  • ToleranceRule types (exactMatch, percentageTolerance, minimumRequired, etc.)
  • SimilarityProfile contexts (DESIGN_PHASE, REPLACEMENT, COST_OPTIMIZATION, etc.)
  • Calculator integration patterns and gotchas

Overview

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).

Core Interfaces

SimilarityCalculator (Simple)

public interface SimilarityCalculator {
    double calculateSimilarity(String normalizedMpn1, String normalizedMpn2);
}

Used for generic calculators that don't need component type context.

ComponentSimilarityCalculator (Type-Aware)

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.

Standard Similarity Thresholds

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

Available Calculators

CalculatorInterfaceComponent TypesKey Features
ResistorSimilarityCalculator
ComponentRESISTOR, RESISTOR_*Value, package, tolerance
CapacitorSimilarityCalculator
ComponentCAPACITOR, CAPACITOR_*Value, voltage, dielectric
TransistorSimilarityCalculator
ComponentTRANSISTOR, TRANSISTOR_*NPN/PNP polarity, equivalent groups
DiodeSimilarityCalculator
ComponentDIODE, DIODE_*Signal/rectifier/zener types
MosfetSimilarityCalculator
ComponentMOSFET, MOSFET_*N/P channel, equivalent groups
OpAmpSimilarityCalculator
ComponentOPAMP, OPAMP_*Single/dual/quad, equivalent families
VoltageRegulatorSimilarityCalculator
ComponentVOLTAGE_REGULATOR*Fixed (78xx) vs adjustable (LM317)
LogicICSimilarityCalculator
ComponentLOGIC_IC, IC74xx/CD4000 series, function groups
LEDSimilarityCalculator
ComponentLED, LED_*Color, bins, families
MemorySimilarityCalculator
ComponentMEMORY, MEMORY_*I2C/SPI EEPROM, Flash equivalents
SensorSimilarityCalculator
ComponentSENSOR, TEMPERATURE_SENSOR, ACCELEROMETERSensor families, package variants
ConnectorSimilarityCalculator
ComponentCONNECTOR, CONNECTOR_*Pin count, pitch, family
MicrocontrollerSimilarityCalculator
ComponentMICROCONTROLLER*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

Creating a New Similarity Calculator

1. Implement the Interface

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;
    }
}

2. Key Design Principles

  1. Return 0.0 for null inputs - Always check for null MPNs and registry
  2. Return 0.0 for non-matching types - If the MPN isn't your component type
  3. Use equivalent groups - Define known equivalent parts (e.g., 2N2222 ≈ PN2222)
  4. Consider package variants - Same part in different package should be high similarity
  5. Ensure symmetry -
    sim(A,B) == sim(B,A)
  6. Keep in [0.0, 1.0] - Never return values outside this range

3. Common Patterns

Equivalent Groups

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;
}

Package Code Extraction

private String extractBasePart(String mpn) {
    // Remove common package suffixes
    return mpn.replaceAll("(?:CT|T|N|P|DG|PW|DR)$", "");
}

Polarity/Type Checking

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;
}

Testing

Test Structure

@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 { /* ... */ }

Run Tests

# All similarity calculator tests
mvn test -Dtest="*SimilarityCalculatorTest,PassiveComponentCalculatorTest"

# Specific calculator
mvn test -Dtest=TransistorSimilarityCalculatorTest

Related Skills

  • /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)

Metadata-Driven Architecture (January 2026)

The similarity system now uses a metadata-driven architecture for configurable, type-specific similarity rules.

Conversion Status: 12 of 17 calculators converted (71% complete)

CalculatorStatusPRConversion 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#116Jan 2026
MemorySimilarityCalculator✅ Converted#117Jan 2026
LEDSimilarityCalculator✅ Converted#118Jan 2026
ConnectorSimilarityCalculator✅ Converted(pre-existing)Jan 2026
LogicICSimilarityCalculator✅ Converted#119Jan 2026
SensorSimilarityCalculator✅ Converted#120Jan 2026
MicrocontrollerSimilarityCalculator⏳ Legacy--
MCUSimilarityCalculator⏳ Legacy--
PassiveComponentCalculator⏳ Legacy--
LevenshteinCalculator⏳ Legacy--
DefaultSimilarityCalculator⏳ Legacy--

Core Metadata Classes

ClassPurpose
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)

Retrieving Metadata

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();
}

Pre-Registered Types (10)

RESISTOR, CAPACITOR, MOSFET, TRANSISTOR, DIODE, OPAMP, MICROCONTROLLER, MEMORY, LED, CONNECTOR

Each type has:

  • Critical specs (must match for similarity)
  • High/Medium/Low importance specs (contribute to score)
  • Tolerance rules (how to compare values)
  • Default similarity profile

Context-Aware Profiles

Adjust importance multipliers based on use case:

ProfileThresholdCRITICALHIGHMEDIUMLOWUse Case
DESIGN_PHASE0.851.00.90.70.4Exact match for new designs
REPLACEMENT0.751.00.70.40.2Default: Direct replacement
COST_OPTIMIZATION0.601.00.40.20.0Maintain critical specs only
EMERGENCY_SOURCING0.500.80.40.20.0Urgent, 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

Converted Calculator Implementation Pattern

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:

  1. Dual-path approach - Try metadata first, fall back to legacy
  2. Short-circuit checks - Early return for CRITICAL spec mismatches
  3. Weighted scoring -
    totalScore / maxPossibleScore
    formula
  4. Spec extraction - Component-specific methods to extract values from MPNs
  5. Equivalent boost - Apply known equivalence rules after scoring
  6. Profile support - Use
    getEffectiveWeight()
    for context-aware weights

Converted Calculator Specs

CalculatorCritical SpecsHigh ImportanceMedium ImportanceLow Importance
OpAmpconfigurationfamilypackage-
MemorymemoryType, capacityinterface-package
LEDcolorfamily, brightness-package
ConnectorpinCount, pitchfamilymountingType-
LogicICfunctionseries, technology-package
SensorsensorTypefamilyinterfacepackage

Migration Path

For converting existing calculators:

  1. Add imports:
    SimilarityProfile
    ,
    ToleranceRule
    ,
    SpecUnit
    ,
    SpecValue
  2. Modify
    calculateSimilarity()
    to check for metadata first
  3. Implement
    calculateMetadataDrivenSimilarity()
    method
  4. Add spec extraction helper methods
  5. Update tests to use threshold assertions (
    >= HIGH_SIMILARITY
    )
  6. Run full test suite to verify backward compatibility

For new calculators:

  1. Start with metadata-driven approach from the beginning
  2. Define specs in
    ComponentTypeMetadataRegistry
  3. Implement spec extraction methods
  4. Use
    SpecValue
    wrapper for type-safe comparison
  5. Apply context-aware profiles with
    SimilarityProfile

See CLAUDE.md § "Metadata-Driven Similarity Framework" for complete architecture details.


Learnings & Quirks

General Patterns

  • Always normalize MPNs to uppercase before comparison
  • Package suffixes vary by manufacturer - don't assume consistency
  • Some calculators return HIGH_SIMILARITY (0.9) for identical parts, not 1.0

Edge Cases

  • Parts starting with digits (e.g., "2N2222") need special regex handling -
    ^[A-Za-z]+
    won't match
  • Some MPNs have significant hyphens (Molex) vs decorative hyphens (TI)
  • Reel/tape suffixes (-RL, -T, -TR) should generally be ignored

Metadata System Gotchas (January 2026)

SpecValue 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:

  • Registry is shared across all tests
  • Custom registrations persist
  • Use unregistered types (CRYSTAL, FUSE) for tests, not RESISTOR/CAPACITOR

Profile Multiplier Values:

  • COST_OPTIMIZATION maintains CRITICAL=1.0 (safety specs never compromised)
  • EMERGENCY_SOURCING relaxes CRITICAL to 0.8 (only for urgent scenarios)

Builder Validation:

ComponentTypeMetadata.builder(null).build(); // IllegalArgumentException
ComponentTypeMetadata.builder(ComponentType.IC).build(); // IllegalStateException (no specs)
<!-- Add new learnings above this line -->

See Also

Advanced Skills

  • /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 calculators