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.
Files changed (62) hide show
  1. {qnty-0.1.4 → qnty-0.1.5}/PKG-INFO +1 -1
  2. {qnty-0.1.4 → qnty-0.1.5}/pyproject.toml +1 -1
  3. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/expressions/functions.py +18 -2
  4. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/expressions/nodes.py +44 -6
  5. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/composition.py +28 -26
  6. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/units/field_units.py +1 -1
  7. {qnty-0.1.4 → qnty-0.1.5}/README.md +0 -0
  8. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/__init__.py +0 -0
  9. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/constants/__init__.py +0 -0
  10. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/constants/numerical.py +0 -0
  11. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/constants/solvers.py +0 -0
  12. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/constants/tests.py +0 -0
  13. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/dimensions/__init__.py +0 -0
  14. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/dimensions/base.py +0 -0
  15. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/dimensions/field_dims.py +0 -0
  16. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/dimensions/field_dims.pyi +0 -0
  17. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/dimensions/signature.py +0 -0
  18. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/equations/__init__.py +0 -0
  19. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/equations/equation.py +0 -0
  20. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/equations/system.py +0 -0
  21. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/expressions/__init__.py +0 -0
  22. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/expressions/formatter.py +0 -0
  23. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/expressions/types.py +0 -0
  24. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/extensions/__init__.py +0 -0
  25. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/extensions/integration/__init__.py +0 -0
  26. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/extensions/plotting/__init__.py +0 -0
  27. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/extensions/reporting/__init__.py +0 -0
  28. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/__init__.py +0 -0
  29. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/problem.py +0 -0
  30. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/rules.py +0 -0
  31. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/solving.py +0 -0
  32. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/problems/validation.py +0 -0
  33. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/__init__.py +0 -0
  34. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/base_qnty.py +0 -0
  35. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_converters.py +0 -0
  36. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_qnty.py +0 -0
  37. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_setter.py +0 -0
  38. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_setter.pyi +0 -0
  39. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_vars.py +0 -0
  40. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/quantities/field_vars.pyi +0 -0
  41. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/__init__.py +0 -0
  42. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/manager.py +0 -0
  43. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/order.py +0 -0
  44. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/solvers/__init__.py +0 -0
  45. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/solvers/base.py +0 -0
  46. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/solvers/iterative.py +0 -0
  47. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/solving/solvers/simultaneous.py +0 -0
  48. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/units/__init__.py +0 -0
  49. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/units/field_units.pyi +0 -0
  50. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/units/prefixes.py +0 -0
  51. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/units/registry.py +0 -0
  52. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/__init__.py +0 -0
  53. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/caching/__init__.py +0 -0
  54. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/caching/manager.py +0 -0
  55. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/error_handling/__init__.py +0 -0
  56. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/error_handling/context.py +0 -0
  57. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/error_handling/exceptions.py +0 -0
  58. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/error_handling/handlers.py +0 -0
  59. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/logging.py +0 -0
  60. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/protocols.py +0 -0
  61. {qnty-0.1.4 → qnty-0.1.5}/src/qnty/utils/scope_discovery.py +0 -0
  62. {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.4
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "qnty"
3
- version = "0.1.4"
3
+ version = "0.1.5"
4
4
  description = "High-performance unit system library for Python with dimensional safety and fast unit conversions"
5
5
  readme = "README.md"
6
6
  license = { text = "Apache-2.0" }
@@ -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) -> UnaryFunction:
20
- return UnaryFunction(name, wrap_operand(expr))
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: Quantity(math.sin(x.value), DimensionlessUnits.dimensionless),
518
- "cos": lambda x: Quantity(math.cos(x.value), DimensionlessUnits.dimensionless),
519
- "tan": lambda x: Quantity(math.tan(x.value), DimensionlessUnits.dimensionless),
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: Quantity(math.log(x.value), DimensionlessUnits.dimensionless),
523
- "log10": lambda x: Quantity(math.log10(x.value), DimensionlessUnits.dimensionless),
524
- "exp": lambda x: Quantity(math.exp(x.value), DimensionlessUnits.dimensionless),
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
  """
@@ -248,7 +248,7 @@ class AnglePlaneUnits:
248
248
  name="degree",
249
249
  symbol="${ }^{\\circ}$",
250
250
  dimension=dim.ANGLE_PLANE,
251
- si_factor=0.0174533,
251
+ si_factor=0.017453292519943295,
252
252
  si_offset=0.0,
253
253
  ))
254
254
 
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