qnty 0.0.9__py3-none-any.whl → 0.1.1__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.
- qnty/__init__.py +2 -3
- qnty/constants/__init__.py +10 -0
- qnty/constants/numerical.py +18 -0
- qnty/constants/solvers.py +6 -0
- qnty/constants/tests.py +6 -0
- qnty/dimensions/__init__.py +23 -0
- qnty/dimensions/base.py +97 -0
- qnty/dimensions/field_dims.py +126 -0
- qnty/dimensions/field_dims.pyi +128 -0
- qnty/dimensions/signature.py +111 -0
- qnty/equations/__init__.py +1 -1
- qnty/equations/equation.py +118 -155
- qnty/equations/system.py +68 -65
- qnty/expressions/__init__.py +25 -46
- qnty/expressions/formatter.py +188 -0
- qnty/expressions/functions.py +46 -68
- qnty/expressions/nodes.py +540 -384
- qnty/expressions/types.py +70 -0
- qnty/problems/__init__.py +145 -0
- qnty/problems/composition.py +1101 -0
- qnty/problems/problem.py +737 -0
- qnty/problems/rules.py +145 -0
- qnty/problems/solving.py +1216 -0
- qnty/problems/validation.py +127 -0
- qnty/quantities/__init__.py +28 -5
- qnty/quantities/base_qnty.py +677 -0
- qnty/quantities/field_converters.py +24004 -0
- qnty/quantities/field_qnty.py +1012 -0
- qnty/{generated/setters.py → quantities/field_setter.py} +3071 -2961
- qnty/{generated/quantities.py → quantities/field_vars.py} +829 -444
- qnty/{generated/quantities.pyi → quantities/field_vars.pyi} +1289 -1290
- qnty/solving/manager.py +50 -44
- qnty/solving/order.py +181 -133
- qnty/solving/solvers/__init__.py +2 -9
- qnty/solving/solvers/base.py +27 -37
- qnty/solving/solvers/iterative.py +115 -135
- qnty/solving/solvers/simultaneous.py +93 -165
- qnty/units/__init__.py +1 -0
- qnty/{generated/units.py → units/field_units.py} +1700 -991
- qnty/units/field_units.pyi +2461 -0
- qnty/units/prefixes.py +58 -105
- qnty/units/registry.py +76 -89
- qnty/utils/__init__.py +16 -0
- qnty/utils/caching/__init__.py +23 -0
- qnty/utils/caching/manager.py +401 -0
- qnty/utils/error_handling/__init__.py +66 -0
- qnty/utils/error_handling/context.py +39 -0
- qnty/utils/error_handling/exceptions.py +96 -0
- qnty/utils/error_handling/handlers.py +171 -0
- qnty/utils/logging.py +4 -4
- qnty/utils/protocols.py +164 -0
- qnty/utils/scope_discovery.py +420 -0
- {qnty-0.0.9.dist-info → qnty-0.1.1.dist-info}/METADATA +1 -1
- qnty-0.1.1.dist-info/RECORD +60 -0
- qnty/_backup/problem_original.py +0 -1251
- qnty/_backup/quantity.py +0 -63
- qnty/codegen/cli.py +0 -125
- qnty/codegen/generators/data/unit_data.json +0 -8807
- qnty/codegen/generators/data_processor.py +0 -345
- qnty/codegen/generators/dimensions_gen.py +0 -434
- qnty/codegen/generators/doc_generator.py +0 -141
- qnty/codegen/generators/out/dimension_mapping.json +0 -974
- qnty/codegen/generators/out/dimension_metadata.json +0 -123
- qnty/codegen/generators/out/units_metadata.json +0 -223
- qnty/codegen/generators/quantities_gen.py +0 -159
- qnty/codegen/generators/setters_gen.py +0 -178
- qnty/codegen/generators/stubs_gen.py +0 -167
- qnty/codegen/generators/units_gen.py +0 -295
- qnty/expressions/cache.py +0 -94
- qnty/generated/dimensions.py +0 -514
- qnty/problem/__init__.py +0 -91
- qnty/problem/base.py +0 -142
- qnty/problem/composition.py +0 -385
- qnty/problem/composition_mixin.py +0 -382
- qnty/problem/equations.py +0 -413
- qnty/problem/metaclass.py +0 -302
- qnty/problem/reconstruction.py +0 -1016
- qnty/problem/solving.py +0 -180
- qnty/problem/validation.py +0 -64
- qnty/problem/variables.py +0 -239
- qnty/quantities/expression_quantity.py +0 -314
- qnty/quantities/quantity.py +0 -428
- qnty/quantities/typed_quantity.py +0 -215
- qnty/validation/__init__.py +0 -0
- qnty/validation/registry.py +0 -0
- qnty/validation/rules.py +0 -167
- qnty-0.0.9.dist-info/RECORD +0 -63
- /qnty/{codegen → extensions}/__init__.py +0 -0
- /qnty/{codegen/generators → extensions/integration}/__init__.py +0 -0
- /qnty/{codegen/generators/utils → extensions/plotting}/__init__.py +0 -0
- /qnty/{generated → extensions/reporting}/__init__.py +0 -0
- {qnty-0.0.9.dist-info → qnty-0.1.1.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.
|
29
|
-
return base_name if
|
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.
|
33
|
-
return base_symbol if
|
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
|
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
|
-
#
|
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
|
-
|
127
|
-
|
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').
|
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').
|
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 =
|
145
|
-
"""Get a prefix by its multiplication factor.
|
146
|
-
#
|
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
|
-
#
|
151
|
-
if tolerance >
|
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
|
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
|
-
#
|
169
|
+
# Units that should get automatic prefixes
|
168
170
|
PREFIXABLE_UNITS: dict[str, list[StandardPrefixes]] = {
|
169
171
|
# Base SI units
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
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
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
],
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
],
|
198
|
-
|
199
|
-
|
200
|
-
|
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
|
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 ..
|
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
|
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
|
24
|
-
|
24
|
+
prefix: SIPrefix | None = None # SI prefix if applicable
|
25
|
+
|
25
26
|
@classmethod
|
26
|
-
def with_prefix(cls, base_def:
|
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
|
41
|
-
|
42
|
-
__slots__ = (
|
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
|
-
"""
|
58
|
-
|
59
|
-
|
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
|
61
|
+
"""Enable unit constants as dictionary keys."""
|
63
62
|
return self._hash_cache
|
64
63
|
|
65
64
|
|
66
65
|
class Registry:
|
67
|
-
"""
|
68
|
-
|
69
|
-
__slots__ = (
|
70
|
-
|
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
|
-
#
|
77
|
+
# Cache for frequently used conversions
|
80
78
|
self._conversion_cache: dict[tuple[str, str], float] = {}
|
81
|
-
# Cache for common dimension mappings
|
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
|
88
|
+
|
89
|
+
# Group by dimension
|
95
90
|
dim_sig = unit_def.dimension._signature
|
96
|
-
|
91
|
+
if dim_sig in self.dimensional_groups:
|
97
92
|
self.dimensional_groups[dim_sig].append(unit_def)
|
98
|
-
|
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
|
137
|
-
self.conversion_table.clear()
|
138
|
-
self._conversion_cache.clear()
|
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
|
-
|
142
|
-
if group_size <= 1:
|
132
|
+
if len(group) <= 1:
|
143
133
|
continue # Skip groups with single units
|
144
|
-
|
145
|
-
#
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
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
|
-
"""
|
160
|
-
#
|
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
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
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
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
factor =
|
184
|
-
if len(
|
185
|
-
|
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
|
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
|
+
]
|