qnty 0.1.4__tar.gz → 0.1.5__tar.gz
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-0.1.4 → qnty-0.1.5}/PKG-INFO +1 -1
- {qnty-0.1.4 → qnty-0.1.5}/pyproject.toml +1 -1
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/expressions/functions.py +18 -2
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/expressions/nodes.py +44 -6
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/composition.py +28 -26
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/units/field_units.py +1 -1
- {qnty-0.1.4 → qnty-0.1.5}/README.md +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/constants/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/constants/numerical.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/constants/solvers.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/constants/tests.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/dimensions/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/dimensions/base.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/dimensions/field_dims.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/dimensions/field_dims.pyi +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/dimensions/signature.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/equations/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/equations/equation.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/equations/system.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/expressions/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/expressions/formatter.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/expressions/types.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/extensions/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/extensions/integration/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/extensions/plotting/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/extensions/reporting/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/problem.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/rules.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/solving.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/validation.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/base_qnty.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_converters.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_qnty.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_setter.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_setter.pyi +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_vars.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_vars.pyi +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/manager.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/order.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/solvers/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/solvers/base.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/solvers/iterative.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/solvers/simultaneous.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/units/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/units/field_units.pyi +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/units/prefixes.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/units/registry.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/caching/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/caching/manager.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/error_handling/__init__.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/error_handling/context.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/error_handling/exceptions.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/error_handling/handlers.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/logging.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/protocols.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/scope_discovery.py +0 -0
- {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/unit_suggestions.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: qnty
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.5
|
4
4
|
Summary: High-performance unit system library for Python with dimensional safety and fast unit conversions
|
5
5
|
License: Apache-2.0
|
6
6
|
Keywords: units,dimensional analysis,engineering,physics,quantities,measurements
|
@@ -16,8 +16,24 @@ ConditionalOperand = Expression | BinaryOperation
|
|
16
16
|
def _create_unary_function(name: str, docstring: str):
|
17
17
|
"""Factory function for creating unary mathematical functions."""
|
18
18
|
|
19
|
-
def func(expr: ExpressionOperand)
|
20
|
-
|
19
|
+
def func(expr: ExpressionOperand):
|
20
|
+
from .nodes import VariableReference
|
21
|
+
|
22
|
+
wrapped_expr = wrap_operand(expr)
|
23
|
+
|
24
|
+
# For known quantities (FieldQnty with known values), evaluate immediately
|
25
|
+
if hasattr(expr, 'quantity') and expr.quantity is not None:
|
26
|
+
try:
|
27
|
+
unary_func = UnaryFunction(name, wrapped_expr)
|
28
|
+
# Use an empty variable dict since we have the quantity directly
|
29
|
+
result = unary_func.evaluate({})
|
30
|
+
return result
|
31
|
+
except (ValueError, TypeError, AttributeError):
|
32
|
+
# Fall back to expression if evaluation fails
|
33
|
+
pass
|
34
|
+
|
35
|
+
# For unknown variables or expressions, return the UnaryFunction
|
36
|
+
return UnaryFunction(name, wrapped_expr)
|
21
37
|
|
22
38
|
func.__name__ = name
|
23
39
|
func.__doc__ = docstring
|
@@ -514,14 +514,14 @@ class UnaryFunction(Expression):
|
|
514
514
|
|
515
515
|
# Function dispatch table for better performance and maintainability
|
516
516
|
functions = {
|
517
|
-
"sin": lambda x:
|
518
|
-
"cos": lambda x:
|
519
|
-
"tan": lambda x:
|
517
|
+
"sin": lambda x: math.sin(self._to_radians_if_angle(x)),
|
518
|
+
"cos": lambda x: math.cos(self._to_radians_if_angle(x)),
|
519
|
+
"tan": lambda x: math.tan(self._to_radians_if_angle(x)),
|
520
520
|
"sqrt": lambda x: Quantity(math.sqrt(x.value), x.unit),
|
521
521
|
"abs": lambda x: Quantity(abs(x.value), x.unit),
|
522
|
-
"ln": lambda x:
|
523
|
-
"log10": lambda x:
|
524
|
-
"exp": lambda x:
|
522
|
+
"ln": lambda x: math.log(x.value),
|
523
|
+
"log10": lambda x: math.log10(x.value),
|
524
|
+
"exp": lambda x: math.exp(x.value),
|
525
525
|
}
|
526
526
|
|
527
527
|
func = functions.get(self.function_name)
|
@@ -530,6 +530,35 @@ class UnaryFunction(Expression):
|
|
530
530
|
else:
|
531
531
|
raise ValueError(f"Unknown function: {self.function_name}")
|
532
532
|
|
533
|
+
def _to_radians_if_angle(self, quantity: "Quantity") -> float:
|
534
|
+
"""Convert angle quantities to radians for trigonometric functions."""
|
535
|
+
from ..dimensions import field_dims
|
536
|
+
|
537
|
+
# Check if this is an angle dimension by comparing dimension signature
|
538
|
+
# Need to handle the case where angle dimensions might not exactly match due to implementation details
|
539
|
+
try:
|
540
|
+
# Import angle plane dimension for comparison
|
541
|
+
angle_plane_dim = field_dims.ANGLE_PLANE
|
542
|
+
|
543
|
+
# If this looks like an angle (has angle dimension or unit name suggests it)
|
544
|
+
if (hasattr(quantity, '_dimension_sig') and quantity._dimension_sig == angle_plane_dim) or \
|
545
|
+
(hasattr(quantity, 'unit') and hasattr(quantity.unit, 'name') and
|
546
|
+
any(angle_word in str(quantity.unit.name).lower() for angle_word in ['degree', 'radian', 'grad', 'gon'])):
|
547
|
+
|
548
|
+
# Import the radian unit for conversion
|
549
|
+
from ..units.field_units import AnglePlaneUnits
|
550
|
+
|
551
|
+
# Convert to radians and return the numeric value
|
552
|
+
radian_quantity = quantity.to(AnglePlaneUnits.radian.definition)
|
553
|
+
return radian_quantity.value
|
554
|
+
else:
|
555
|
+
# Non-angle quantities: return raw value (backward compatibility)
|
556
|
+
return quantity.value
|
557
|
+
except (ImportError, AttributeError, ValueError):
|
558
|
+
# If anything goes wrong with angle detection/conversion, fall back to raw value
|
559
|
+
return quantity.value
|
560
|
+
|
561
|
+
|
533
562
|
def get_variables(self) -> set[str]:
|
534
563
|
return self.operand.get_variables()
|
535
564
|
|
@@ -546,6 +575,15 @@ class UnaryFunction(Expression):
|
|
546
575
|
return UnaryFunction(self.function_name, simplified_operand)
|
547
576
|
|
548
577
|
def __str__(self) -> str:
|
578
|
+
# Try auto-evaluation first if possible
|
579
|
+
can_eval, variables = self._can_auto_evaluate()
|
580
|
+
if can_eval and variables:
|
581
|
+
try:
|
582
|
+
result = self.evaluate(variables)
|
583
|
+
return str(result)
|
584
|
+
except (ValueError, TypeError, AttributeError):
|
585
|
+
# Fall back to symbolic representation
|
586
|
+
pass
|
549
587
|
return ExpressionFormatter.format_unary_function(self) # type: ignore[arg-type]
|
550
588
|
|
551
589
|
|
@@ -186,6 +186,8 @@ class ConfigurableVariable:
|
|
186
186
|
return self._variable._arithmetic_mode
|
187
187
|
# Otherwise default to 'expression'
|
188
188
|
return 'expression'
|
189
|
+
|
190
|
+
|
189
191
|
return getattr(self._variable, name)
|
190
192
|
|
191
193
|
# Delegate arithmetic operations to the wrapped variable
|
@@ -233,10 +235,10 @@ class ConfigurableVariable:
|
|
233
235
|
def __ge__(self, other):
|
234
236
|
return self._variable.__ge__(other)
|
235
237
|
|
236
|
-
def __eq__(self, other):
|
238
|
+
def __eq__(self, other): # type: ignore[override]
|
237
239
|
return self._variable.__eq__(other)
|
238
240
|
|
239
|
-
def __ne__(self, other):
|
241
|
+
def __ne__(self, other): # type: ignore[override]
|
240
242
|
return self._variable.__ne__(other)
|
241
243
|
|
242
244
|
def __setattr__(self, name, value):
|
@@ -257,6 +259,30 @@ class ConfigurableVariable:
|
|
257
259
|
else:
|
258
260
|
return original_setter
|
259
261
|
|
262
|
+
def update(self, value=None, unit=None, quantity=None, is_known=None):
|
263
|
+
"""Override update method to track configuration changes."""
|
264
|
+
result = self._variable.update(value, unit, quantity, is_known)
|
265
|
+
if self._proxy and self._original_symbol:
|
266
|
+
# Track this configuration change
|
267
|
+
self._proxy.track_configuration(self._original_symbol, self._variable.quantity, self._variable.is_known)
|
268
|
+
return result
|
269
|
+
|
270
|
+
def mark_known(self):
|
271
|
+
"""Override mark_known to track configuration changes."""
|
272
|
+
result = self._variable.mark_known()
|
273
|
+
if self._proxy and self._original_symbol:
|
274
|
+
# Track this configuration change
|
275
|
+
self._proxy.track_configuration(self._original_symbol, self._variable.quantity, self._variable.is_known)
|
276
|
+
return result
|
277
|
+
|
278
|
+
def mark_unknown(self):
|
279
|
+
"""Override mark_unknown to track configuration changes."""
|
280
|
+
result = self._variable.mark_unknown()
|
281
|
+
if self._proxy and self._original_symbol:
|
282
|
+
# Track this configuration change
|
283
|
+
self._proxy.track_configuration(self._original_symbol, self._variable.quantity, self._variable.is_known)
|
284
|
+
return result
|
285
|
+
|
260
286
|
|
261
287
|
class TrackingSetterWrapper:
|
262
288
|
"""
|
@@ -307,30 +333,6 @@ class TrackingSetterWrapper:
|
|
307
333
|
)
|
308
334
|
return result
|
309
335
|
|
310
|
-
def update(self, value=None, unit=None, quantity=None, is_known=None):
|
311
|
-
"""Override update method to track configuration changes."""
|
312
|
-
result = self._variable.update(value, unit, quantity, is_known)
|
313
|
-
if self._proxy and self._original_symbol:
|
314
|
-
# Track this configuration change
|
315
|
-
self._proxy.track_configuration(self._original_symbol, self._variable.quantity, self._variable.is_known)
|
316
|
-
return result
|
317
|
-
|
318
|
-
def mark_known(self):
|
319
|
-
"""Override mark_known to track configuration changes."""
|
320
|
-
result = self._variable.mark_known()
|
321
|
-
if self._proxy and self._original_symbol:
|
322
|
-
# Track this configuration change
|
323
|
-
self._proxy.track_configuration(self._original_symbol, self._variable.quantity, self._variable.is_known)
|
324
|
-
return result
|
325
|
-
|
326
|
-
def mark_unknown(self):
|
327
|
-
"""Override mark_unknown to track configuration changes."""
|
328
|
-
result = self._variable.mark_unknown()
|
329
|
-
if self._proxy and self._original_symbol:
|
330
|
-
# Track this configuration change
|
331
|
-
self._proxy.track_configuration(self._original_symbol, self._variable.quantity, self._variable.is_known)
|
332
|
-
return result
|
333
|
-
|
334
336
|
|
335
337
|
class DelayedVariableReference(ArithmeticOperationsMixin):
|
336
338
|
"""
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|