| name | similarity |
| description | Use when working with component similarity calculations - comparing MPNs, finding equivalent parts, implementing new similarity calculators, or understanding how component matching works. |
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;
private static final double MEDIUM_SIMILARITY = 0.7;
private static final double LOW_SIMILARITY = 0.3;
Available Calculators
| 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 |
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;
if (!isComponentType(mpn1) || !isComponentType(mpn2)) {
return 0.0;
}
return similarity;
}
}
2. Key Design Principles
- Return 0.0 for null inputs - Always check for null MPNs and registry
- Return 0.0 for non-matching types - If the MPN isn't your component type
- Use equivalent groups - Define known equivalent parts (e.g., 2N2222 ≈ PN2222)
- Consider package variants - Same part in different package should be high similarity
- Ensure symmetry -
sim(A,B) == sim(B,A)
- 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) {
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
mvn test -Dtest="*SimilarityCalculatorTest,PassiveComponentCalculatorTest"
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)
| 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 | - | - |
Core Metadata Classes
| 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) |
Retrieving Metadata
ComponentTypeMetadataRegistry registry = ComponentTypeMetadataRegistry.getInstance();
Optional<ComponentTypeMetadata> metadata = registry.getMetadata(ComponentType.RESISTOR);
if (metadata.isPresent()) {
ComponentTypeMetadata meta = metadata.get();
boolean critical = meta.isCritical("resistance");
SpecConfig config = meta.getSpecConfig("resistance");
if (config != null) {
ToleranceRule rule = config.getToleranceRule();
SpecImportance importance = config.getImportance();
}
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:
| 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 |
SimilarityProfile profile = SimilarityProfile.REPLACEMENT;
double similarity = 0.78;
boolean passes = profile.meetsThreshold(similarity);
double effectiveWeight = profile.getEffectiveWeight(SpecImportance.HIGH);
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;
Optional<ComponentTypeMetadata> metadataOpt = metadataRegistry.getMetadata(ComponentType.OPAMP);
if (metadataOpt.isPresent()) {
logger.trace("Using metadata-driven similarity calculation");
return calculateMetadataDrivenSimilarity(mpn1, mpn2, metadataOpt.get());
}
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();
String config1 = extractConfiguration(mpn1);
String config2 = extractConfiguration(mpn2);
if (!config1.isEmpty() && !config2.isEmpty() && !config1.equals(config2)) {
return LOW_SIMILARITY;
}
double totalScore = 0.0;
double maxPossibleScore = 0.0;
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;
}
double similarity = maxPossibleScore > 0 ? totalScore / maxPossibleScore : 0.0;
if (areEquivalentParts(mpn1, mpn2)) {
similarity = Math.max(similarity, HIGH_SIMILARITY);
}
return similarity;
}
Key Features of Converted Calculators:
- Dual-path approach - Try metadata first, fall back to legacy
- Short-circuit checks - Early return for CRITICAL spec mismatches
- Weighted scoring -
totalScore / maxPossibleScore formula
- Spec extraction - Component-specific methods to extract values from MPNs
- Equivalent boost - Apply known equivalence rules after scoring
- Profile support - Use
getEffectiveWeight() for context-aware weights
Converted Calculator Specs
| 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 |
Migration Path
For converting existing calculators:
- Add imports:
SimilarityProfile, ToleranceRule, SpecUnit, SpecValue
- Modify
calculateSimilarity() to check for metadata first
- Implement
calculateMetadataDrivenSimilarity() method
- Add spec extraction helper methods
- Update tests to use threshold assertions (
>= HIGH_SIMILARITY)
- Run full test suite to verify backward compatibility
For new calculators:
- Start with metadata-driven approach from the beginning
- Define specs in
ComponentTypeMetadataRegistry
- Implement spec extraction methods
- Use
SpecValue wrapper for type-safe comparison
- 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:
SpecValue<Double> v = new SpecValue<>(100.0, SpecUnit.FARAD);
SpecValue<Double> v = SpecValue.of(100.0);
API Return Types:
Optional<ComponentTypeMetadata> meta = registry.getMetadata(type);
SpecConfig config = metadata.getSpecConfig("resistance");
boolean critical = metadata.isCritical("resistance");
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();
ComponentTypeMetadata.builder(ComponentType.IC).build();
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