qnty 0.0.9__py3-none-any.whl → 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. qnty/__init__.py +2 -3
  2. qnty/constants/__init__.py +10 -0
  3. qnty/constants/numerical.py +18 -0
  4. qnty/constants/solvers.py +6 -0
  5. qnty/constants/tests.py +6 -0
  6. qnty/dimensions/__init__.py +23 -0
  7. qnty/dimensions/base.py +97 -0
  8. qnty/dimensions/field_dims.py +126 -0
  9. qnty/dimensions/field_dims.pyi +128 -0
  10. qnty/dimensions/signature.py +111 -0
  11. qnty/equations/__init__.py +1 -1
  12. qnty/equations/equation.py +118 -155
  13. qnty/equations/system.py +68 -65
  14. qnty/expressions/__init__.py +25 -46
  15. qnty/expressions/formatter.py +188 -0
  16. qnty/expressions/functions.py +46 -68
  17. qnty/expressions/nodes.py +539 -384
  18. qnty/expressions/types.py +70 -0
  19. qnty/problems/__init__.py +145 -0
  20. qnty/problems/composition.py +1031 -0
  21. qnty/problems/problem.py +695 -0
  22. qnty/problems/rules.py +145 -0
  23. qnty/problems/solving.py +1216 -0
  24. qnty/problems/validation.py +127 -0
  25. qnty/quantities/__init__.py +28 -5
  26. qnty/quantities/base_qnty.py +677 -0
  27. qnty/quantities/field_converters.py +24004 -0
  28. qnty/quantities/field_qnty.py +1012 -0
  29. qnty/{generated/setters.py → quantities/field_setter.py} +3071 -2961
  30. qnty/{generated/quantities.py → quantities/field_vars.py} +754 -432
  31. qnty/{generated/quantities.pyi → quantities/field_vars.pyi} +1289 -1290
  32. qnty/solving/manager.py +50 -44
  33. qnty/solving/order.py +181 -133
  34. qnty/solving/solvers/__init__.py +2 -9
  35. qnty/solving/solvers/base.py +27 -37
  36. qnty/solving/solvers/iterative.py +115 -135
  37. qnty/solving/solvers/simultaneous.py +93 -165
  38. qnty/units/__init__.py +1 -0
  39. qnty/{generated/units.py → units/field_units.py} +1700 -991
  40. qnty/units/field_units.pyi +2461 -0
  41. qnty/units/prefixes.py +58 -105
  42. qnty/units/registry.py +76 -89
  43. qnty/utils/__init__.py +16 -0
  44. qnty/utils/caching/__init__.py +23 -0
  45. qnty/utils/caching/manager.py +401 -0
  46. qnty/utils/error_handling/__init__.py +66 -0
  47. qnty/utils/error_handling/context.py +39 -0
  48. qnty/utils/error_handling/exceptions.py +96 -0
  49. qnty/utils/error_handling/handlers.py +171 -0
  50. qnty/utils/logging.py +4 -4
  51. qnty/utils/protocols.py +164 -0
  52. qnty/utils/scope_discovery.py +420 -0
  53. {qnty-0.0.9.dist-info → qnty-0.1.0.dist-info}/METADATA +1 -1
  54. qnty-0.1.0.dist-info/RECORD +60 -0
  55. qnty/_backup/problem_original.py +0 -1251
  56. qnty/_backup/quantity.py +0 -63
  57. qnty/codegen/cli.py +0 -125
  58. qnty/codegen/generators/data/unit_data.json +0 -8807
  59. qnty/codegen/generators/data_processor.py +0 -345
  60. qnty/codegen/generators/dimensions_gen.py +0 -434
  61. qnty/codegen/generators/doc_generator.py +0 -141
  62. qnty/codegen/generators/out/dimension_mapping.json +0 -974
  63. qnty/codegen/generators/out/dimension_metadata.json +0 -123
  64. qnty/codegen/generators/out/units_metadata.json +0 -223
  65. qnty/codegen/generators/quantities_gen.py +0 -159
  66. qnty/codegen/generators/setters_gen.py +0 -178
  67. qnty/codegen/generators/stubs_gen.py +0 -167
  68. qnty/codegen/generators/units_gen.py +0 -295
  69. qnty/expressions/cache.py +0 -94
  70. qnty/generated/dimensions.py +0 -514
  71. qnty/problem/__init__.py +0 -91
  72. qnty/problem/base.py +0 -142
  73. qnty/problem/composition.py +0 -385
  74. qnty/problem/composition_mixin.py +0 -382
  75. qnty/problem/equations.py +0 -413
  76. qnty/problem/metaclass.py +0 -302
  77. qnty/problem/reconstruction.py +0 -1016
  78. qnty/problem/solving.py +0 -180
  79. qnty/problem/validation.py +0 -64
  80. qnty/problem/variables.py +0 -239
  81. qnty/quantities/expression_quantity.py +0 -314
  82. qnty/quantities/quantity.py +0 -428
  83. qnty/quantities/typed_quantity.py +0 -215
  84. qnty/validation/__init__.py +0 -0
  85. qnty/validation/registry.py +0 -0
  86. qnty/validation/rules.py +0 -167
  87. qnty-0.0.9.dist-info/RECORD +0 -63
  88. /qnty/{codegen → extensions}/__init__.py +0 -0
  89. /qnty/{codegen/generators → extensions/integration}/__init__.py +0 -0
  90. /qnty/{codegen/generators/utils → extensions/plotting}/__init__.py +0 -0
  91. /qnty/{generated → extensions/reporting}/__init__.py +0 -0
  92. {qnty-0.0.9.dist-info → qnty-0.1.0.dist-info}/WHEEL +0 -0
qnty/units/prefixes.py CHANGED
@@ -9,36 +9,40 @@ Provides systematic handling of metric prefixes like kilo-, milli-, micro-, etc.
9
9
  from dataclasses import dataclass
10
10
  from enum import Enum
11
11
 
12
+ from ..constants.numerical import PREFIX_LOOKUP_MIN_TOLERANCE, PREFIX_LOOKUP_TOLERANCE
13
+
12
14
 
13
15
  @dataclass(frozen=True, slots=True)
14
16
  class SIPrefix:
15
17
  """
16
18
  Standard SI prefix definition.
17
-
19
+
18
20
  Attributes:
19
21
  name: Full prefix name (e.g., "kilo", "milli")
20
22
  symbol: Standard symbol (e.g., "k", "m")
21
23
  factor: Multiplication factor relative to base unit (e.g., 1000, 0.001)
22
24
  """
25
+
23
26
  name: str
24
27
  symbol: str
25
28
  factor: float
26
-
29
+
27
30
  def apply_to_name(self, base_name: str) -> str:
28
- """Apply prefix to a base unit name. Optimized for performance."""
29
- return base_name if not self.name else self.name + base_name
30
-
31
+ """Apply prefix to a base unit name."""
32
+ return self.name + base_name if self.name else base_name
33
+
31
34
  def apply_to_symbol(self, base_symbol: str) -> str:
32
- """Apply prefix to a base unit symbol. Optimized for performance."""
33
- return base_symbol if not self.symbol else self.symbol + base_symbol
35
+ """Apply prefix to a base unit symbol."""
36
+ return self.symbol + base_symbol if self.symbol else base_symbol
34
37
 
35
38
 
36
39
  class StandardPrefixes(Enum):
37
40
  """
38
41
  Standard SI prefixes with their multiplication factors.
39
-
42
+
40
43
  From yotta (10^24) to yocto (10^-24).
41
44
  """
45
+
42
46
  # Larger prefixes (10^3 to 10^24)
43
47
  YOTTA = SIPrefix("yotta", "Y", 1e24)
44
48
  ZETTA = SIPrefix("zetta", "Z", 1e21)
@@ -50,10 +54,10 @@ class StandardPrefixes(Enum):
50
54
  KILO = SIPrefix("kilo", "k", 1e3)
51
55
  HECTO = SIPrefix("hecto", "h", 1e2)
52
56
  DECA = SIPrefix("deca", "da", 1e1)
53
-
57
+
54
58
  # Base (no prefix)
55
59
  NONE = SIPrefix("", "", 1.0)
56
-
60
+
57
61
  # Smaller prefixes (10^-1 to 10^-24)
58
62
  DECI = SIPrefix("deci", "d", 1e-1)
59
63
  CENTI = SIPrefix("centi", "c", 1e-2)
@@ -67,7 +71,7 @@ class StandardPrefixes(Enum):
67
71
  YOCTO = SIPrefix("yocto", "y", 1e-24)
68
72
 
69
73
 
70
- # Common prefix groups for different unit types - type annotated for better IDE support
74
+ # Common prefix groups for different unit types
71
75
  COMMON_LENGTH_PREFIXES: list[StandardPrefixes] = [
72
76
  StandardPrefixes.KILO,
73
77
  StandardPrefixes.CENTI,
@@ -118,38 +122,39 @@ COMMON_PRESSURE_PREFIXES: list[StandardPrefixes] = [
118
122
  ]
119
123
 
120
124
 
121
- # Performance optimization: Pre-computed lookup dictionaries
125
+ # Lookup dictionaries for fast prefix searches
122
126
  _NAME_TO_PREFIX: dict[str, SIPrefix] = {}
123
127
  _SYMBOL_TO_PREFIX: dict[str, SIPrefix] = {}
124
128
  _FACTOR_TO_PREFIX: dict[float, SIPrefix] = {}
125
129
 
126
- def _initialize_lookup_caches():
127
- """Initialize lookup caches for O(1) prefix lookups."""
130
+
131
+ def _initialize_lookup_caches() -> None:
132
+ """Initialize lookup caches for fast prefix lookups."""
128
133
  for prefix_enum in StandardPrefixes:
129
134
  prefix = prefix_enum.value
130
135
  _NAME_TO_PREFIX[prefix.name] = prefix
131
136
  _SYMBOL_TO_PREFIX[prefix.symbol] = prefix
132
137
  _FACTOR_TO_PREFIX[prefix.factor] = prefix
133
138
 
139
+
134
140
  def get_prefix_by_name(name: str) -> SIPrefix | None:
135
- """Get a prefix by its name (e.g., 'kilo', 'milli'). O(1) lookup."""
141
+ """Get a prefix by its name (e.g., 'kilo', 'milli')."""
136
142
  return _NAME_TO_PREFIX.get(name)
137
143
 
138
144
 
139
145
  def get_prefix_by_symbol(symbol: str) -> SIPrefix | None:
140
- """Get a prefix by its symbol (e.g., 'k', 'm'). O(1) lookup."""
146
+ """Get a prefix by its symbol (e.g., 'k', 'm')."""
141
147
  return _SYMBOL_TO_PREFIX.get(symbol)
142
148
 
143
149
 
144
- def get_prefix_by_factor(factor: float, tolerance: float = 1e-10) -> SIPrefix | None:
145
- """Get a prefix by its multiplication factor. O(1) lookup for exact matches, optimized tolerance search."""
146
- # Fast path for exact matches
150
+ def get_prefix_by_factor(factor: float, tolerance: float = PREFIX_LOOKUP_TOLERANCE) -> SIPrefix | None:
151
+ """Get a prefix by its multiplication factor."""
152
+ # Check for exact match first
147
153
  if factor in _FACTOR_TO_PREFIX:
148
154
  return _FACTOR_TO_PREFIX[factor]
149
-
150
- # Optimized tolerance path - only search if tolerance is meaningful
151
- if tolerance > 1e-15: # Avoid expensive search for tiny tolerances
152
- # Use items() view for better performance than .items()
155
+
156
+ # Search with tolerance if meaningful
157
+ if tolerance > PREFIX_LOOKUP_MIN_TOLERANCE:
153
158
  for cached_factor, prefix in _FACTOR_TO_PREFIX.items():
154
159
  if abs(cached_factor - factor) < tolerance:
155
160
  return prefix
@@ -157,94 +162,42 @@ def get_prefix_by_factor(factor: float, tolerance: float = 1e-10) -> SIPrefix |
157
162
 
158
163
 
159
164
  def extract_prefix_values(prefix_enums: list[StandardPrefixes]) -> list[SIPrefix]:
160
- """Extract SIPrefix values from StandardPrefixes enums efficiently.
161
-
162
- This is optimized for bulk operations that need to convert enum lists to value lists.
163
- """
165
+ """Extract SIPrefix values from StandardPrefixes enums."""
164
166
  return [prefix_enum.value for prefix_enum in prefix_enums]
165
167
 
166
168
 
167
- # Define which units should get automatic prefixes - using more descriptive type annotation
169
+ # Units that should get automatic prefixes
168
170
  PREFIXABLE_UNITS: dict[str, list[StandardPrefixes]] = {
169
171
  # Base SI units
170
- 'meter': COMMON_LENGTH_PREFIXES,
171
- 'gram': COMMON_MASS_PREFIXES,
172
- 'second': COMMON_TIME_PREFIXES,
173
- 'ampere': COMMON_ELECTRIC_PREFIXES,
174
- 'kelvin': [], # Temperature usually doesn't use prefixes
175
- 'mole': [
176
- StandardPrefixes.MILLI,
177
- StandardPrefixes.MICRO
178
- ],
179
- 'candela': [], # Luminous intensity rarely uses prefixes
180
-
172
+ "meter": COMMON_LENGTH_PREFIXES,
173
+ "gram": COMMON_MASS_PREFIXES,
174
+ "second": COMMON_TIME_PREFIXES,
175
+ "ampere": COMMON_ELECTRIC_PREFIXES,
176
+ "kelvin": [], # Temperature usually doesn't use prefixes
177
+ "mole": [StandardPrefixes.MILLI, StandardPrefixes.MICRO],
178
+ "candela": [], # Luminous intensity rarely uses prefixes
181
179
  # Derived SI units
182
- 'pascal': COMMON_PRESSURE_PREFIXES,
183
- 'joule': COMMON_ENERGY_PREFIXES,
184
- 'watt': COMMON_POWER_PREFIXES,
185
- 'coulomb': COMMON_ELECTRIC_PREFIXES,
186
- 'volt': COMMON_ELECTRIC_PREFIXES,
187
- 'farad': [
188
- StandardPrefixes.MILLI,
189
- StandardPrefixes.MICRO,
190
- StandardPrefixes.NANO,
191
- StandardPrefixes.PICO
192
- ],
193
- 'ohm': [
194
- StandardPrefixes.KILO,
195
- StandardPrefixes.MEGA,
196
- StandardPrefixes.MILLI
197
- ],
198
- 'siemens': [
199
- StandardPrefixes.MILLI,
200
- StandardPrefixes.MICRO
201
- ],
202
- 'weber': [
203
- StandardPrefixes.MILLI,
204
- StandardPrefixes.MICRO
205
- ],
206
- 'tesla': [
207
- StandardPrefixes.MILLI,
208
- StandardPrefixes.MICRO,
209
- StandardPrefixes.NANO
210
- ],
211
- 'henry': [
212
- StandardPrefixes.MILLI,
213
- StandardPrefixes.MICRO,
214
- StandardPrefixes.NANO
215
- ],
216
- 'lumen': [],
217
- 'lux': [],
218
- 'becquerel': [
219
- StandardPrefixes.KILO,
220
- StandardPrefixes.MEGA,
221
- StandardPrefixes.GIGA
222
- ],
223
- 'gray': [
224
- StandardPrefixes.MILLI,
225
- StandardPrefixes.MICRO
226
- ],
227
- 'sievert': [
228
- StandardPrefixes.MILLI,
229
- StandardPrefixes.MICRO
230
- ],
231
- 'hertz': [
232
- StandardPrefixes.KILO,
233
- StandardPrefixes.MEGA,
234
- StandardPrefixes.GIGA
235
- ],
236
- 'newton': [
237
- StandardPrefixes.KILO,
238
- StandardPrefixes.MILLI
239
- ],
240
- 'bar': [
241
- StandardPrefixes.MILLI
242
- ], # Common non-SI unit
243
- 'liter': [
244
- StandardPrefixes.MILLI,
245
- StandardPrefixes.MICRO
246
- ], # Common non-SI unit
180
+ "pascal": COMMON_PRESSURE_PREFIXES,
181
+ "joule": COMMON_ENERGY_PREFIXES,
182
+ "watt": COMMON_POWER_PREFIXES,
183
+ "coulomb": COMMON_ELECTRIC_PREFIXES,
184
+ "volt": COMMON_ELECTRIC_PREFIXES,
185
+ "farad": [StandardPrefixes.MILLI, StandardPrefixes.MICRO, StandardPrefixes.NANO, StandardPrefixes.PICO],
186
+ "ohm": [StandardPrefixes.KILO, StandardPrefixes.MEGA, StandardPrefixes.MILLI],
187
+ "siemens": [StandardPrefixes.MILLI, StandardPrefixes.MICRO],
188
+ "weber": [StandardPrefixes.MILLI, StandardPrefixes.MICRO],
189
+ "tesla": [StandardPrefixes.MILLI, StandardPrefixes.MICRO, StandardPrefixes.NANO],
190
+ "henry": [StandardPrefixes.MILLI, StandardPrefixes.MICRO, StandardPrefixes.NANO],
191
+ "lumen": [],
192
+ "lux": [],
193
+ "becquerel": [StandardPrefixes.KILO, StandardPrefixes.MEGA, StandardPrefixes.GIGA],
194
+ "gray": [StandardPrefixes.MILLI, StandardPrefixes.MICRO],
195
+ "sievert": [StandardPrefixes.MILLI, StandardPrefixes.MICRO],
196
+ "hertz": [StandardPrefixes.KILO, StandardPrefixes.MEGA, StandardPrefixes.GIGA],
197
+ "newton": [StandardPrefixes.KILO, StandardPrefixes.MILLI],
198
+ "bar": [StandardPrefixes.MILLI], # Common non-SI unit
199
+ "liter": [StandardPrefixes.MILLI, StandardPrefixes.MICRO], # Common non-SI unit
247
200
  }
248
201
 
249
- # Initialize lookup caches on module load for optimal performance
202
+ # Initialize lookup caches on module load
250
203
  _initialize_lookup_caches()
qnty/units/registry.py CHANGED
@@ -7,23 +7,24 @@ Unit definitions, constants and registry for the high-performance unit system.
7
7
 
8
8
  from dataclasses import dataclass
9
9
 
10
- from ..generated.dimensions import DimensionSignature
10
+ from ..dimensions import DimensionSignature
11
11
  from .prefixes import SIPrefix, StandardPrefixes
12
12
 
13
13
 
14
14
  @dataclass(frozen=True, slots=True)
15
15
  class UnitDefinition:
16
- """Immutable unit definition optimized for performance."""
16
+ """Immutable unit definition for the unit system."""
17
+
17
18
  name: str
18
19
  symbol: str
19
20
  dimension: DimensionSignature
20
21
  si_factor: float
21
22
  si_offset: float = 0.0
22
23
  base_unit_name: str | None = None # Base unit without prefix
23
- prefix: SIPrefix | None = None # SI prefix if applicable
24
-
24
+ prefix: SIPrefix | None = None # SI prefix if applicable
25
+
25
26
  @classmethod
26
- def with_prefix(cls, base_def: 'UnitDefinition', prefix: SIPrefix) -> 'UnitDefinition':
27
+ def with_prefix(cls, base_def: "UnitDefinition", prefix: SIPrefix) -> "UnitDefinition":
27
28
  """Create a new unit definition with an SI prefix."""
28
29
  return cls(
29
30
  name=prefix.apply_to_name(base_def.name),
@@ -32,43 +33,40 @@ class UnitDefinition:
32
33
  si_factor=base_def.si_factor * prefix.factor,
33
34
  si_offset=base_def.si_offset,
34
35
  base_unit_name=base_def.name,
35
- prefix=prefix
36
+ prefix=prefix,
36
37
  )
37
38
 
38
39
 
39
40
  class UnitConstant:
40
- """Unit constant that provides type safety and performance."""
41
-
42
- __slots__ = ('definition', 'name', 'symbol', 'dimension', 'si_factor', '_hash_cache')
43
-
41
+ """Unit constant that provides type safety."""
42
+
43
+ __slots__ = ("definition", "name", "symbol", "dimension", "si_factor", "_hash_cache")
44
+
44
45
  def __init__(self, definition: UnitDefinition):
45
46
  self.definition = definition
46
47
  self.name = definition.name
47
48
  self.symbol = definition.symbol
48
49
  self.dimension = definition.dimension
49
50
  self.si_factor = definition.si_factor
50
- # Cache expensive hash operation
51
51
  self._hash_cache = hash(self.name)
52
-
53
- def __str__(self):
52
+
53
+ def __str__(self) -> str:
54
54
  return self.symbol
55
-
55
+
56
56
  def __eq__(self, other) -> bool:
57
- """Ultra-fast equality check for unit constants."""
58
- # Fast path: check type first without isinstance() overhead
59
- return type(other) is UnitConstant and self.name == other.name
60
-
57
+ """Equality check for unit constants."""
58
+ return isinstance(other, UnitConstant) and self.name == other.name
59
+
61
60
  def __hash__(self) -> int:
62
- """Enable unit constants as dictionary keys with cached hash."""
61
+ """Enable unit constants as dictionary keys."""
63
62
  return self._hash_cache
64
63
 
65
64
 
66
65
  class Registry:
67
- """Ultra-fast registry with pre-computed conversion tables."""
68
-
69
- __slots__ = ('units', 'conversion_table', 'dimensional_groups', '_finalized',
70
- 'base_units', 'prefixable_units', '_conversion_cache', '_dimension_cache')
71
-
66
+ """Unit registry with pre-computed conversion tables."""
67
+
68
+ __slots__ = ("units", "conversion_table", "dimensional_groups", "_finalized", "base_units", "prefixable_units", "_conversion_cache", "_dimension_cache")
69
+
72
70
  def __init__(self):
73
71
  self.units: dict[str, UnitDefinition] = {}
74
72
  self.conversion_table: dict[tuple[str, str], float] = {} # (from_unit, to_unit) -> factor
@@ -76,48 +74,41 @@ class Registry:
76
74
  self._finalized = False
77
75
  self.base_units: dict[str, UnitDefinition] = {} # Track base units for prefix generation
78
76
  self.prefixable_units: set[str] = set() # Track which units can have prefixes
79
- # Small cache for frequently used conversions to reduce table lookups
77
+ # Cache for frequently used conversions
80
78
  self._conversion_cache: dict[tuple[str, str], float] = {}
81
- # Cache for common dimension mappings (used by variable.py)
79
+ # Cache for common dimension mappings
82
80
  self._dimension_cache: dict[int | float, UnitConstant] = {}
83
81
 
84
- # Registry starts empty - units are registered via register_all_units() in __init__.py
85
-
86
-
87
82
  def register_unit(self, unit_def: UnitDefinition) -> None:
88
83
  """Register a single unit definition."""
89
84
  if self._finalized:
90
85
  raise RuntimeError("Cannot register units after registry is finalized")
91
-
86
+
92
87
  self.units[unit_def.name] = unit_def
93
-
94
- # Group by dimension - optimized to avoid repeated signature access
88
+
89
+ # Group by dimension
95
90
  dim_sig = unit_def.dimension._signature
96
- try:
91
+ if dim_sig in self.dimensional_groups:
97
92
  self.dimensional_groups[dim_sig].append(unit_def)
98
- except KeyError:
93
+ else:
99
94
  self.dimensional_groups[dim_sig] = [unit_def]
100
-
101
- def register_with_prefixes(
102
- self,
103
- unit_def: UnitDefinition,
104
- prefixes: list[StandardPrefixes] | None = None
105
- ) -> None:
95
+
96
+ def register_with_prefixes(self, unit_def: UnitDefinition, prefixes: list[StandardPrefixes] | None = None) -> None:
106
97
  """
107
98
  Register a unit and automatically generate prefixed variants.
108
-
99
+
109
100
  Args:
110
101
  unit_def: The base unit definition
111
102
  prefixes: List of StandardPrefixes enum values to apply. If None, uses common prefixes.
112
103
  """
113
104
  if self._finalized:
114
105
  raise RuntimeError("Cannot register units after registry is finalized")
115
-
106
+
116
107
  # Register base unit
117
108
  self.register_unit(unit_def)
118
109
  self.base_units[unit_def.name] = unit_def
119
110
  self.prefixable_units.add(unit_def.name)
120
-
111
+
121
112
  # Generate and register prefixed variants
122
113
  if prefixes:
123
114
  for prefix_enum in prefixes:
@@ -125,7 +116,7 @@ class Registry:
125
116
  if prefix.name: # Skip NONE prefix (empty name)
126
117
  prefixed_unit = UnitDefinition.with_prefix(unit_def, prefix)
127
118
  self.register_unit(prefixed_unit)
128
-
119
+
129
120
  def finalize_registration(self) -> None:
130
121
  """Called after all units registered to precompute conversions."""
131
122
  if not self._finalized:
@@ -133,58 +124,54 @@ class Registry:
133
124
  self._finalized = True
134
125
 
135
126
  def _precompute_conversions(self) -> None:
136
- """Pre-compute all unit conversions for maximum speed with optimized algorithms."""
137
- self.conversion_table.clear() # Clear existing conversions
138
- self._conversion_cache.clear() # Clear cache
139
-
127
+ """Pre-compute all unit conversions for fast lookup."""
128
+ self.conversion_table.clear()
129
+ self._conversion_cache.clear()
130
+
140
131
  for group in self.dimensional_groups.values():
141
- group_size = len(group)
142
- if group_size <= 1:
132
+ if len(group) <= 1:
143
133
  continue # Skip groups with single units
144
-
145
- # Ultra-optimized: pre-compute all factors and names in single pass
146
- unit_data = [(unit.name, unit.si_factor) for unit in group]
147
-
148
- # Vectorized approach: compute all combinations efficiently
149
- for i in range(group_size):
150
- from_name, from_si = unit_data[i]
151
- for j in range(group_size):
152
- if i != j: # Skip same unit conversion
153
- to_name, to_si = unit_data[j]
154
- # Pre-compute factor - avoid repeated division
155
- factor = from_si / to_si
156
- self.conversion_table[(from_name, to_name)] = factor
157
-
134
+
135
+ # Compute conversion factors for all unit pairs
136
+ for from_unit in group:
137
+ for to_unit in group:
138
+ if from_unit != to_unit:
139
+ factor = from_unit.si_factor / to_unit.si_factor
140
+ self.conversion_table[(from_unit.name, to_unit.name)] = factor
141
+
158
142
  def convert(self, value: float, from_unit: UnitConstant, to_unit: UnitConstant) -> float:
159
- """Ultra-fast conversion with optimized equality check and caching."""
160
- # Ultra-fast path: avoid expensive equality check by comparing names directly
143
+ """Convert a value between units with optimized lookups."""
144
+ # ULTRA-FAST PATH: Same unit - no conversion needed (most common case)
161
145
  if from_unit.name == to_unit.name:
162
146
  return value
163
-
164
- key = (from_unit.name, to_unit.name)
165
-
166
- # Check small cache first for frequently used conversions
167
- try:
168
- return value * self._conversion_cache[key]
169
- except KeyError:
170
- pass
171
-
172
- # O(1) lookup for pre-computed conversions
173
- try:
174
- factor = self.conversion_table[key]
175
- # Cache frequently used conversions (keep cache small)
176
- if len(self._conversion_cache) < 50:
177
- self._conversion_cache[key] = factor
147
+
148
+ # OPTIMIZATION: Extract names once to avoid repeated attribute access
149
+ from_name = from_unit.name
150
+ to_name = to_unit.name
151
+ key = (from_name, to_name)
152
+
153
+ # STREAMLINED CACHE: Direct dictionary access with batched operations
154
+ conversion_cache = self._conversion_cache
155
+ if key in conversion_cache:
156
+ return value * conversion_cache[key]
157
+
158
+ # OPTIMIZED LOOKUP: Direct table access
159
+ conversion_table = self.conversion_table
160
+ if key in conversion_table:
161
+ factor = conversion_table[key]
162
+ # Cache frequently used conversions - direct assignment for speed
163
+ if len(conversion_cache) < 50:
164
+ conversion_cache[key] = factor
178
165
  return value * factor
179
- except KeyError:
180
- pass
181
-
182
- # Fallback (shouldn't happen for registered units)
183
- factor = from_unit.si_factor / to_unit.si_factor
184
- if len(self._conversion_cache) < 50:
185
- self._conversion_cache[key] = factor
166
+
167
+ # FAST FALLBACK: Direct SI factor calculation with caching
168
+ from_si = from_unit.si_factor
169
+ to_si = to_unit.si_factor
170
+ factor = from_si / to_si
171
+ if len(conversion_cache) < 50:
172
+ conversion_cache[key] = factor
186
173
  return value * factor
187
174
 
188
175
 
189
- # Global high-performance registry
176
+ # Global unit registry
190
177
  registry = Registry()
qnty/utils/__init__.py CHANGED
@@ -0,0 +1,16 @@
1
+ """Shared utilities and helper functions."""
2
+
3
+ from .protocols import ExpressionProtocol, TypeRegistry, VariableProtocol, is_expression, is_variable, register_expression_type, register_variable_type
4
+ from .scope_discovery import ScopeDiscoveryService, discover_variables_from_scope
5
+
6
+ __all__ = [
7
+ "ScopeDiscoveryService",
8
+ "discover_variables_from_scope",
9
+ "TypeRegistry",
10
+ "ExpressionProtocol",
11
+ "VariableProtocol",
12
+ "register_expression_type",
13
+ "register_variable_type",
14
+ "is_expression",
15
+ "is_variable",
16
+ ]
@@ -0,0 +1,23 @@
1
+ """
2
+ Caching utilities for the qnty library.
3
+
4
+ Provides centralized cache management for improved performance and memory efficiency.
5
+ """
6
+
7
+ from .manager import (
8
+ CacheStats,
9
+ UnifiedCacheManager,
10
+ clear_all_caches,
11
+ get_cache_manager,
12
+ get_cache_statistics,
13
+ get_memory_usage,
14
+ )
15
+
16
+ __all__ = [
17
+ "UnifiedCacheManager",
18
+ "CacheStats",
19
+ "get_cache_manager",
20
+ "clear_all_caches",
21
+ "get_cache_statistics",
22
+ "get_memory_usage",
23
+ ]