
similarity
by Cantara
SKILL.md
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; // 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
| 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;
// 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
- 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) {
// 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)
| 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();
// 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:
| 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
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:
- Dual-path approach - Try metadata first, fall back to legacy
- Short-circuit checks - Early return for CRITICAL spec mismatches
- Weighted scoring -
totalScore / maxPossibleScoreformula - 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
SpecValuewrapper 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:
// 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)
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
スコア
総合スコア
リポジトリの品質指標に基づく評価
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
レビュー
レビュー機能は近日公開予定です