qnty 0.0.8__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 (74) hide show
  1. qnty/__init__.py +140 -59
  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 +4 -0
  12. qnty/equations/equation.py +220 -0
  13. qnty/equations/system.py +130 -0
  14. qnty/expressions/__init__.py +40 -0
  15. qnty/expressions/formatter.py +188 -0
  16. qnty/expressions/functions.py +74 -0
  17. qnty/expressions/nodes.py +701 -0
  18. qnty/expressions/types.py +70 -0
  19. qnty/extensions/plotting/__init__.py +0 -0
  20. qnty/extensions/reporting/__init__.py +0 -0
  21. qnty/problems/__init__.py +145 -0
  22. qnty/problems/composition.py +1031 -0
  23. qnty/problems/problem.py +695 -0
  24. qnty/problems/rules.py +145 -0
  25. qnty/problems/solving.py +1216 -0
  26. qnty/problems/validation.py +127 -0
  27. qnty/quantities/__init__.py +29 -0
  28. qnty/quantities/base_qnty.py +677 -0
  29. qnty/quantities/field_converters.py +24004 -0
  30. qnty/quantities/field_qnty.py +1012 -0
  31. qnty/quantities/field_setter.py +12320 -0
  32. qnty/quantities/field_vars.py +6325 -0
  33. qnty/quantities/field_vars.pyi +4191 -0
  34. qnty/solving/__init__.py +0 -0
  35. qnty/solving/manager.py +96 -0
  36. qnty/solving/order.py +403 -0
  37. qnty/solving/solvers/__init__.py +13 -0
  38. qnty/solving/solvers/base.py +82 -0
  39. qnty/solving/solvers/iterative.py +165 -0
  40. qnty/solving/solvers/simultaneous.py +475 -0
  41. qnty/units/__init__.py +1 -0
  42. qnty/units/field_units.py +10507 -0
  43. qnty/units/field_units.pyi +2461 -0
  44. qnty/units/prefixes.py +203 -0
  45. qnty/{unit.py → units/registry.py} +89 -61
  46. qnty/utils/__init__.py +16 -0
  47. qnty/utils/caching/__init__.py +23 -0
  48. qnty/utils/caching/manager.py +401 -0
  49. qnty/utils/error_handling/__init__.py +66 -0
  50. qnty/utils/error_handling/context.py +39 -0
  51. qnty/utils/error_handling/exceptions.py +96 -0
  52. qnty/utils/error_handling/handlers.py +171 -0
  53. qnty/utils/logging.py +40 -0
  54. qnty/utils/protocols.py +164 -0
  55. qnty/utils/scope_discovery.py +420 -0
  56. qnty-0.1.0.dist-info/METADATA +199 -0
  57. qnty-0.1.0.dist-info/RECORD +60 -0
  58. qnty/dimension.py +0 -186
  59. qnty/equation.py +0 -297
  60. qnty/expression.py +0 -553
  61. qnty/prefixes.py +0 -229
  62. qnty/unit_types/base.py +0 -47
  63. qnty/units.py +0 -8113
  64. qnty/variable.py +0 -300
  65. qnty/variable_types/base.py +0 -58
  66. qnty/variable_types/expression_variable.py +0 -106
  67. qnty/variable_types/typed_variable.py +0 -87
  68. qnty/variables.py +0 -2298
  69. qnty/variables.pyi +0 -6148
  70. qnty-0.0.8.dist-info/METADATA +0 -355
  71. qnty-0.0.8.dist-info/RECORD +0 -19
  72. /qnty/{unit_types → extensions}/__init__.py +0 -0
  73. /qnty/{variable_types → extensions/integration}/__init__.py +0 -0
  74. {qnty-0.0.8.dist-info → qnty-0.1.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,420 @@
1
+ """
2
+ Scope Discovery Service
3
+ =======================
4
+
5
+ Centralized service for automatically discovering variables from the calling scope.
6
+ Consolidates all scope inspection logic used across expressions, equations, and variable solving.
7
+
8
+ This module uses protocol-based design to avoid circular imports and duck typing performance issues.
9
+ """
10
+
11
+ import inspect
12
+ import logging
13
+ from typing import Any
14
+
15
+ from .protocols import TypeRegistry
16
+
17
+ # Setup logging for better debugging
18
+ _logger = logging.getLogger(__name__)
19
+
20
+
21
+ class ScopeDiscoveryService:
22
+ """
23
+ Centralized service for scope discovery operations.
24
+
25
+ Provides optimized variable discovery from calling scopes with caching,
26
+ depth limits, and consistent error handling.
27
+ """
28
+
29
+ # Class-level optimization settings
30
+ _scope_cache = {}
31
+ _frame_cache = {}
32
+ _variable_name_cache = {}
33
+ _max_scope_cache_size = 200 # Increased cache size
34
+ _max_frame_cache_size = 50
35
+ _max_search_depth = 8
36
+ _cache_hit_count = 0
37
+ _cache_miss_count = 0
38
+
39
+ @classmethod
40
+ def discover_variables(cls, required_vars: set[str], enable_caching: bool = True) -> dict[str, Any]:
41
+ """
42
+ Discover variables from the calling scope with enhanced caching.
43
+
44
+ Args:
45
+ required_vars: Set of variable names to find
46
+ enable_caching: Whether to use caching for performance
47
+
48
+ Returns:
49
+ Dictionary mapping variable names to variable instances
50
+ """
51
+ if not required_vars:
52
+ return {}
53
+
54
+ # Check cache first if enabled
55
+ if enable_caching:
56
+ cache_key = frozenset(required_vars)
57
+ if cache_key in cls._scope_cache:
58
+ cls._cache_hit_count += 1
59
+ _logger.debug(f"Cache hit for variables: {required_vars}")
60
+ return cls._scope_cache[cache_key]
61
+
62
+ cls._cache_miss_count += 1
63
+
64
+ # Clean cache if it gets too large (LRU-style)
65
+ if len(cls._scope_cache) >= cls._max_scope_cache_size:
66
+ # Remove oldest 25% of entries
67
+ items_to_remove = len(cls._scope_cache) // 4
68
+ for _ in range(items_to_remove):
69
+ cls._scope_cache.pop(next(iter(cls._scope_cache)))
70
+ _logger.debug(f"Cleaned {items_to_remove} entries from scope cache")
71
+
72
+ # Get the calling frame with caching
73
+ frame = cls._get_cached_user_frame()
74
+ if frame is None:
75
+ _logger.debug("No user frame found")
76
+ return {}
77
+
78
+ try:
79
+ discovered = cls._search_frame_for_variables(frame, required_vars)
80
+
81
+ # Cache the result if caching is enabled and successful
82
+ if enable_caching and required_vars:
83
+ cache_key = frozenset(required_vars)
84
+ cls._scope_cache[cache_key] = discovered
85
+ _logger.debug(f"Cached discovery result for variables: {required_vars}")
86
+
87
+ return discovered
88
+
89
+ except Exception as e:
90
+ _logger.warning(f"Error during variable discovery: {e}")
91
+ return {}
92
+
93
+ @classmethod
94
+ def can_auto_evaluate(cls, expression: Any) -> tuple[bool, dict[str, Any]]:
95
+ """
96
+ Check if expression can be auto-evaluated from scope.
97
+
98
+ Args:
99
+ expression: Expression to check for auto-evaluation
100
+
101
+ Returns:
102
+ Tuple of (can_evaluate, discovered_variables)
103
+ """
104
+ try:
105
+ # Use protocol-based checking instead of duck typing
106
+ if not TypeRegistry.is_expression(expression):
107
+ return False, {}
108
+
109
+ required_vars = expression.get_variables()
110
+ if not required_vars:
111
+ return True, {} # No variables needed, can evaluate
112
+
113
+ discovered = cls.discover_variables(required_vars, enable_caching=True)
114
+
115
+ # Check if all required variables are available and have values
116
+ for var_name in required_vars:
117
+ if var_name not in discovered:
118
+ _logger.debug(f"Variable '{var_name}' not found in scope")
119
+ return False, {}
120
+
121
+ var = discovered[var_name]
122
+ if not hasattr(var, "quantity") or var.quantity is None:
123
+ _logger.debug(f"Variable '{var_name}' has no quantity")
124
+ return False, {}
125
+
126
+ _logger.debug(f"Expression can be auto-evaluated with variables: {list(discovered.keys())}")
127
+ return True, discovered
128
+
129
+ except Exception as e:
130
+ _logger.warning(f"Error during auto-evaluation check: {e}")
131
+ return False, {}
132
+
133
+ @classmethod
134
+ def find_variables_in_scope(cls, filter_func=None) -> dict[str, Any]:
135
+ """
136
+ Find all UnifiedVariable instances in the calling scope.
137
+
138
+ Args:
139
+ filter_func: Optional function to filter variables (var) -> bool
140
+
141
+ Returns:
142
+ Dictionary mapping variable names/symbols to variable instances
143
+ """
144
+ frame = inspect.currentframe()
145
+ if frame is None:
146
+ _logger.warning("Unable to access current frame")
147
+ return {}
148
+
149
+ try:
150
+ frame = cls._find_user_frame(frame)
151
+ if frame is None:
152
+ return {}
153
+
154
+ discovered = {}
155
+
156
+ # Search locals first
157
+ for obj in frame.f_locals.values():
158
+ if TypeRegistry.is_variable(obj):
159
+ if filter_func is None or filter_func(obj):
160
+ var_name = cls._get_variable_name(obj)
161
+ if var_name:
162
+ discovered[var_name] = obj
163
+
164
+ # Search globals for remaining variables
165
+ for obj in frame.f_globals.values():
166
+ if TypeRegistry.is_variable(obj):
167
+ if filter_func is None or filter_func(obj):
168
+ var_name = cls._get_variable_name(obj)
169
+ if var_name and var_name not in discovered:
170
+ discovered[var_name] = obj
171
+
172
+ return discovered
173
+
174
+ finally:
175
+ del frame
176
+
177
+ @classmethod
178
+ def _get_cached_user_frame(cls) -> Any | None:
179
+ """
180
+ Get user frame with caching to reduce repeated frame traversal.
181
+
182
+ Returns:
183
+ User frame or None if not found
184
+ """
185
+ # Get current frame for cache key generation
186
+ current_frame = inspect.currentframe()
187
+ if current_frame is None:
188
+ return None
189
+
190
+ try:
191
+ # Create cache key based on frame signature
192
+ frame_id = id(current_frame)
193
+
194
+ # Check frame cache first
195
+ if frame_id in cls._frame_cache:
196
+ cached_frame = cls._frame_cache[frame_id]
197
+ if cached_frame is not None:
198
+ return cached_frame
199
+
200
+ # Clean frame cache if too large
201
+ if len(cls._frame_cache) >= cls._max_frame_cache_size:
202
+ cls._frame_cache.clear()
203
+
204
+ # Find user frame using optimized search
205
+ user_frame = cls._find_user_frame_optimized(current_frame)
206
+
207
+ # Cache the result
208
+ cls._frame_cache[frame_id] = user_frame
209
+ return user_frame
210
+
211
+ finally:
212
+ del current_frame
213
+
214
+ @classmethod
215
+ def _find_user_frame_optimized(cls, current_frame: Any) -> Any | None:
216
+ """
217
+ Optimized frame search with precompiled patterns.
218
+
219
+ Args:
220
+ current_frame: Starting frame
221
+
222
+ Returns:
223
+ User frame or None if not found within depth limit
224
+ """
225
+ frame = current_frame
226
+ depth = 0
227
+
228
+ # Pre-compiled sets for faster lookups
229
+ internal_file_endings = frozenset(["expression.py", "equation.py", "nodes.py", "scope_discovery.py", "expression_quantity.py", "unified_variable.py", "field_qnty.py"])
230
+ internal_function_names = frozenset(["__str__", "__repr__", "_can_auto_evaluate", "_discover_variables_from_scope", "solve_from", "evaluate"])
231
+
232
+ while frame and depth < cls._max_search_depth:
233
+ code = frame.f_code
234
+ filename = code.co_filename
235
+ function_name = code.co_name
236
+
237
+ # Fast check: is this frame internal?
238
+ is_internal = any(filename.endswith(ending) for ending in internal_file_endings) or function_name in internal_function_names
239
+
240
+ if not is_internal:
241
+ _logger.debug(f"Found user frame at depth {depth}: {filename}:{function_name}")
242
+ return frame
243
+
244
+ frame = frame.f_back
245
+ depth += 1
246
+
247
+ _logger.debug(f"No user frame found within {cls._max_search_depth} levels")
248
+ return None
249
+
250
+ @classmethod
251
+ def _find_user_frame(cls, current_frame: Any) -> Any | None:
252
+ """
253
+ Legacy method for backward compatibility.
254
+ """
255
+ return cls._find_user_frame_optimized(current_frame)
256
+
257
+ @classmethod
258
+ def _search_frame_for_variables(cls, frame: Any, required_vars: set[str]) -> dict[str, Any]:
259
+ """
260
+ Search a specific frame for required variables.
261
+
262
+ Args:
263
+ frame: Frame to search
264
+ required_vars: Set of variable names to find
265
+
266
+ Returns:
267
+ Dictionary of found variables
268
+ """
269
+ discovered = {}
270
+
271
+ # Search locals first (most common case)
272
+ local_vars = frame.f_locals
273
+ for var_name in required_vars:
274
+ # Direct lookup first (fastest)
275
+ if var_name in local_vars:
276
+ obj = local_vars[var_name]
277
+ if TypeRegistry.is_variable(obj):
278
+ discovered[var_name] = obj
279
+ continue
280
+
281
+ # Get globals once for reuse
282
+ global_vars = frame.f_globals
283
+
284
+ # Search globals only for remaining variables
285
+ if len(discovered) < len(required_vars):
286
+ remaining_vars = required_vars - discovered.keys()
287
+ for var_name in remaining_vars:
288
+ if var_name in global_vars:
289
+ obj = global_vars[var_name]
290
+ if TypeRegistry.is_variable(obj):
291
+ discovered[var_name] = obj
292
+
293
+ # Search by symbol/name for remaining variables (optimized)
294
+ if len(discovered) < len(required_vars):
295
+ remaining_vars = required_vars - discovered.keys()
296
+ cls._search_by_symbol_name_optimized(local_vars, global_vars, remaining_vars, discovered)
297
+
298
+ _logger.debug(f"Found {len(discovered)} of {len(required_vars)} required variables")
299
+ return discovered
300
+
301
+ @classmethod
302
+ def _search_by_symbol_name_optimized(cls, local_vars: dict, global_vars: dict, remaining_vars: set[str], discovered: dict[str, Any]) -> None:
303
+ """Optimized search for variables by their symbol/name attribute."""
304
+ # Convert to list once to avoid repeated set operations
305
+ remaining_list = list(remaining_vars)
306
+
307
+ # Search locals by symbol/name with early termination
308
+ for obj in local_vars.values():
309
+ if not remaining_list: # Check list instead of set
310
+ break
311
+ if TypeRegistry.is_variable(obj):
312
+ obj_name = cls._get_variable_name(obj)
313
+ if obj_name in remaining_vars: # Still check set for O(1) lookup
314
+ discovered[obj_name] = obj
315
+ remaining_list.remove(obj_name)
316
+ remaining_vars.remove(obj_name)
317
+
318
+ # Search globals by symbol/name if still needed
319
+ if remaining_list:
320
+ for obj in global_vars.values():
321
+ if not remaining_list:
322
+ break
323
+ if TypeRegistry.is_variable(obj):
324
+ obj_name = cls._get_variable_name(obj)
325
+ if obj_name in remaining_vars:
326
+ discovered[obj_name] = obj
327
+ remaining_list.remove(obj_name)
328
+ remaining_vars.remove(obj_name)
329
+
330
+ @classmethod
331
+ def _search_by_symbol_name(cls, local_vars: dict, global_vars: dict, remaining_vars: set[str], discovered: dict[str, Any]) -> None:
332
+ """Legacy method for backward compatibility."""
333
+ cls._search_by_symbol_name_optimized(local_vars, global_vars, remaining_vars, discovered)
334
+
335
+ @classmethod
336
+ def _get_variable_name(cls, var) -> str | None:
337
+ """
338
+ Get the name/symbol to use for a variable with caching.
339
+
340
+ Args:
341
+ var: Variable instance
342
+
343
+ Returns:
344
+ Variable name/symbol or None if not available
345
+ """
346
+ var_id = id(var)
347
+
348
+ # Check cache first
349
+ if var_id in cls._variable_name_cache:
350
+ return cls._variable_name_cache[var_id]
351
+
352
+ try:
353
+ # Prefer symbol over name for equation solving
354
+ name = var.symbol if var.symbol else var.name
355
+
356
+ # Cache the result
357
+ cls._variable_name_cache[var_id] = name
358
+ return name
359
+ except (AttributeError, TypeError):
360
+ cls._variable_name_cache[var_id] = None
361
+ return None
362
+
363
+ @classmethod
364
+ def clear_cache(cls) -> None:
365
+ """Clear all caches for testing or memory management."""
366
+ cls._scope_cache.clear()
367
+ cls._frame_cache.clear()
368
+ cls._variable_name_cache.clear()
369
+ cls._cache_hit_count = 0
370
+ cls._cache_miss_count = 0
371
+ TypeRegistry.clear_cache()
372
+ _logger.debug("Cleared all scope discovery caches")
373
+
374
+ @classmethod
375
+ def get_cache_stats(cls) -> dict[str, Any]:
376
+ """Get cache performance statistics."""
377
+ total_requests = cls._cache_hit_count + cls._cache_miss_count
378
+ hit_rate = (cls._cache_hit_count / total_requests * 100) if total_requests > 0 else 0
379
+
380
+ return {
381
+ "scope_cache_size": len(cls._scope_cache),
382
+ "frame_cache_size": len(cls._frame_cache),
383
+ "variable_name_cache_size": len(cls._variable_name_cache),
384
+ "cache_hits": cls._cache_hit_count,
385
+ "cache_misses": cls._cache_miss_count,
386
+ "hit_rate_percent": round(hit_rate, 2),
387
+ }
388
+
389
+ @classmethod
390
+ def set_max_depth(cls, depth: int) -> None:
391
+ """Set maximum search depth for scope discovery."""
392
+ if depth > 0:
393
+ cls._max_search_depth = depth
394
+ _logger.debug(f"Set max search depth to {depth}")
395
+ else:
396
+ raise ValueError("Depth must be positive")
397
+
398
+ @classmethod
399
+ def enable_debug_logging(cls) -> None:
400
+ """Enable debug logging for scope discovery operations."""
401
+ logging.getLogger(__name__).setLevel(logging.DEBUG)
402
+
403
+ @classmethod
404
+ def disable_debug_logging(cls) -> None:
405
+ """Disable debug logging for scope discovery operations."""
406
+ logging.getLogger(__name__).setLevel(logging.WARNING)
407
+
408
+
409
+ # Convenience function for backward compatibility
410
+ def discover_variables_from_scope(required_vars: set[str]) -> dict[str, Any]:
411
+ """
412
+ Convenience function to discover variables from scope.
413
+
414
+ Args:
415
+ required_vars: Set of variable names to find
416
+
417
+ Returns:
418
+ Dictionary mapping variable names to variable instances
419
+ """
420
+ return ScopeDiscoveryService.discover_variables(required_vars)
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.3
2
+ Name: qnty
3
+ Version: 0.1.0
4
+ Summary: High-performance unit system library for Python with dimensional safety and fast unit conversions
5
+ License: Apache-2.0
6
+ Keywords: units,dimensional analysis,engineering,physics,quantities,measurements
7
+ Author: tn3wman
8
+ Requires-Python: >=3.11, <3.14
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Classifier: Topic :: Scientific/Engineering :: Physics
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Provides-Extra: benchmark
21
+ Provides-Extra: dev
22
+ Requires-Dist: Pint (>=0.24.4) ; extra == "benchmark"
23
+ Requires-Dist: numpy (>=2.3.2)
24
+ Requires-Dist: pytest (>=8.4.1) ; extra == "dev"
25
+ Requires-Dist: ruff (>=0.1.0) ; extra == "dev"
26
+ Project-URL: Bug Tracker, https://github.com/tn3wman/qnty/issues
27
+ Project-URL: Documentation, https://github.com/tn3wman/qnty#readme
28
+ Project-URL: Homepage, https://github.com/tn3wman/qnty
29
+ Project-URL: Repository, https://github.com/tn3wman/qnty
30
+ Description-Content-Type: text/markdown
31
+
32
+ # Qnty
33
+
34
+ **High-performance unit system library for Python with dimensional safety and fast unit conversions for engineering calculations.**
35
+
36
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
37
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
38
+ [![Development Status](https://img.shields.io/badge/status-beta-orange.svg)](https://pypi.org/project/qnty/)
39
+
40
+ ## ⚠️ Important Disclaimer
41
+
42
+ **🚧 Work in Progress**: Qnty is currently in active development and has not been thoroughly vetted for production engineering calculations. While we strive for accuracy, this library should not be used for critical engineering applications without independent verification.
43
+
44
+ **📐 Accuracy Notice**: The authors are not responsible or liable for incorrect results, calculation errors, or any consequences arising from the use of this library. Always validate calculations independently using established engineering tools and practices.
45
+
46
+ *Use Qnty to help prevent unit errors, but always verify critical calculations through multiple methods.*
47
+
48
+ ---
49
+
50
+ ## ✨ Key Features
51
+
52
+ - **🚀 Ultra-Fast Performance**: Prime number encoding and pre-computed conversion tables
53
+ - **🛡️ Type Safety**: Compile-time dimensional analysis prevents unit errors
54
+ - **⚡ Zero-Cost Abstractions**: Optimized operations with `__slots__` and caching
55
+ - **🔗 Fluent API**: Intuitive method chaining for readable code
56
+ - **🧮 Engineering-Focused**: Built for real-world engineering calculations
57
+ - **🧬 Mathematical System**: Built-in equation solving and expression trees
58
+ - **📊 Comprehensive Testing**: 187 tests with performance benchmarks
59
+
60
+ ## 🚀 Quick Start
61
+
62
+ ### Installation
63
+
64
+ ```bash
65
+ pip install qnty
66
+ ```
67
+
68
+ ### Basic Usage
69
+
70
+ ```python
71
+ from qnty import Length, Pressure, Area
72
+
73
+ # Create quantities with dimensional safety
74
+ width = Length(3, "meter", "Width")
75
+ height = Length(2, "meter", "Height")
76
+
77
+ # Solve mathematical expressions
78
+ area = Area("area", is_known=False)
79
+ area.solve_from(width * height)
80
+ print(f"Area: {area}") # Area: 6.0 m²
81
+ ```
82
+
83
+ ### Engineering Example
84
+
85
+ ```python
86
+ from qnty import Problem, Length, Pressure
87
+
88
+ class PipeThickness(Problem):
89
+ """Calculate pipe wall thickness"""
90
+
91
+ # Known parameters
92
+ pressure = Pressure(150, "pound_force_per_square_inch", "Internal Pressure")
93
+ diameter = Length(6, "inch", "Pipe Diameter")
94
+ allowable_stress = Pressure(20000, "pound_force_per_square_inch", "Allowable Stress")
95
+
96
+ # Unknown to solve for
97
+ thickness = Length("thickness", is_known=False)
98
+
99
+ # Engineering equation: t = (P × D) / (2 × S)
100
+ equation = thickness.equals((pressure * diameter) / (2 * allowable_stress))
101
+
102
+ # Solve the problem
103
+ problem = PipeThickness()
104
+ problem.solve()
105
+ print(f"Required thickness: {problem.thickness}")
106
+ ```
107
+
108
+ ### Mathematical Operations
109
+
110
+ ```python
111
+ from qnty import Length, sqrt, Area
112
+
113
+ # Dimensional analysis with mathematical functions
114
+ area = Area(25, "square_meter", "Square Area")
115
+ side = Length("side", is_known=False)
116
+ side.solve_from(sqrt(area)) # Returns Length, not Area!
117
+ print(f"Side length: {side}") # Side length: 5.0 m
118
+ ```
119
+
120
+ ## 📚 Documentation
121
+
122
+ - **[📖 Tutorial](docs/TUTORIAL.md)** - Step-by-step learning guide
123
+ - **[📋 API Reference](docs/API_REFERENCE.md)** - Complete API documentation
124
+ - **[🏗️ Examples](examples/)** - Real-world engineering examples
125
+ - **[📁 Full Documentation](docs/)** - Complete documentation index
126
+
127
+ ## 🚀 Performance
128
+
129
+ Qnty significantly outperforms other unit libraries with **18.9x average speedup** over Pint:
130
+
131
+ | Operation | Qnty | Pint | **Speedup** |
132
+ |-----------|------|------|-------------|
133
+ | Mixed Unit Addition | 0.76 μs | 17.52 μs | **23.1x** |
134
+ | Complex ASME Equation | 4.07 μs | 106.17 μs | **26.1x** 🚀 |
135
+ | Type-Safe Variables | 0.98 μs | 9.65 μs | **9.8x** |
136
+ | **AVERAGE** | **1.89 μs** | **35.83 μs** | **18.9x** 🏆 |
137
+
138
+ *Run `pytest tests/test_benchmark.py -v -s` to verify on your system.*
139
+
140
+ ## 🧮 100+ Engineering Quantities
141
+
142
+ Qnty provides comprehensive coverage of engineering domains:
143
+
144
+ ```python
145
+ from qnty import (
146
+ # Mechanical
147
+ Length, Area, Volume, Mass, Force, Pressure, Temperature,
148
+ # Electrical
149
+ ElectricPotential, ElectricCurrentIntensity, ElectricResistance,
150
+ # Thermal
151
+ ThermalConductivity, HeatTransferCoefficient,
152
+ # Fluid Dynamics
153
+ ViscosityDynamic, MassFlowRate, VolumetricFlowRate,
154
+ # And 80+ more...
155
+ )
156
+ ```
157
+
158
+ ## 🔧 Development
159
+
160
+ ```bash
161
+ # Install dependencies
162
+ pip install -r requirements.txt
163
+
164
+ # Run tests
165
+ pytest
166
+
167
+ # Run specific test
168
+ pytest tests/test_dimension.py -v
169
+
170
+ # Run benchmarks
171
+ python tests/test_benchmark.py
172
+
173
+ # Lint code
174
+ ruff check src/ tests/
175
+ ruff format src/ tests/
176
+ ```
177
+
178
+ ## 📄 License
179
+
180
+ This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
181
+
182
+ ## 🤝 Contributing
183
+
184
+ We welcome contributions! Please see [CLAUDE.md](CLAUDE.md) for development guidelines and:
185
+
186
+ 1. Fork the repository
187
+ 2. Create a feature branch
188
+ 3. Add tests for new functionality
189
+ 4. Ensure all tests pass: `pytest`
190
+ 5. Submit a pull request
191
+
192
+ ---
193
+
194
+ **Ready to supercharge your engineering calculations?** 🚀
195
+
196
+ - Start with the **[Tutorial](docs/TUTORIAL.md)**
197
+ - Browse the **[API Reference](docs/API_REFERENCE.md)**
198
+ - Try the **[Examples](examples/)**
199
+
@@ -0,0 +1,60 @@
1
+ qnty/__init__.py,sha256=JlMYdpcYFD8agf9nJrxnBw-MXKzGxcqCO4wFxTICQaA,5564
2
+ qnty/constants/__init__.py,sha256=-ADevTOA1tgi5OebYDFzP5ov8jq7Bwy66BCZqiXmGJk,267
3
+ qnty/constants/numerical.py,sha256=zs1OaSV5NDnxHLELzHz_G3NcetuF7JEO0Lih4BGUz60,679
4
+ qnty/constants/solvers.py,sha256=f9bMxxSu99Bg0laWBnLkWbbD5-N02rHiLqlTmO-VSik,212
5
+ qnty/constants/tests.py,sha256=viGxxGBDIu0uA2nJtK20liJ1_ZjqkC20XghxwDaXRv8,218
6
+ qnty/dimensions/__init__.py,sha256=MtHz9Auvpv2xJ4jxRTT7mL1EFk5fuBLJlhxhSRRfSRc,672
7
+ qnty/dimensions/base.py,sha256=YkEn0fvXKq2Utruh2lGAdIT1mVrhu24QXfDl0CMUEG4,2604
8
+ qnty/dimensions/field_dims.py,sha256=bcPu1xxPhjoNjc7TxyP_B4xKDLHKGdtNne-sCB9hz-8,5300
9
+ qnty/dimensions/field_dims.pyi,sha256=lk3YrH3Ovs3CJCZe5MfX334kdpmsfEql4D3fLKjuYDs,4575
10
+ qnty/dimensions/signature.py,sha256=yk7QGejAV-TEPTqWE1Q5yV2sZA-RWGiK_rHiMT0Q2yU,4173
11
+ qnty/equations/__init__.py,sha256=Ou5H6tTFXgVw16JYan_a4653NxroBxcnTY6YWt380Qo,108
12
+ qnty/equations/equation.py,sha256=6Ot3-XhSFyxdv3hUwJvdTvB2BGZlAoXX46uok_6q-14,9041
13
+ qnty/equations/system.py,sha256=vMoD1iTUrAHnVFVvCUKeyNfSBfMiqpwQbfDx46kN9N8,5155
14
+ qnty/expressions/__init__.py,sha256=DA2s7DBhVCmdUgsYSTJWObsp2DbbpFn492yr1nUTg2g,930
15
+ qnty/expressions/formatter.py,sha256=yLGLwLYjhBvVi2Q6rfkg8pbyH0-a1Ko0AYLsqJTJf50,7806
16
+ qnty/expressions/functions.py,sha256=ek43udfUDpThKo38rVPBYPvKfZNc9Bbs8RuL-CvQc_A,2729
17
+ qnty/expressions/nodes.py,sha256=7JxHzhqZNrNUqShIBsIyuLmHQyeC14m4RxPCwxKvmrE,28431
18
+ qnty/expressions/types.py,sha256=eoM-IqY-k-IypRHAlRwjEtMmB6DiwX7YGot8t_vGw3o,1729
19
+ qnty/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ qnty/extensions/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ qnty/extensions/plotting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ qnty/extensions/reporting/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ qnty/problems/__init__.py,sha256=g7zuml2IecoSwgzX6r1zZ5SlmBKFc8qqTR_cao037pc,4808
24
+ qnty/problems/composition.py,sha256=JUOu3IksLRJEJ2OvPyarxkuzvcrnYI9fNeov2Lj7ynk,41937
25
+ qnty/problems/problem.py,sha256=Yh7wLo7RyZcR9fie6niJqcnP81rZhDKtNTgd5KQr1XE,28279
26
+ qnty/problems/rules.py,sha256=NwIStAa8bocVtvzAsnPmRdC_0ENTJWyXLOoYBnkvpPA,5176
27
+ qnty/problems/solving.py,sha256=LTI8F9ujDiSqXE9Aiz8sOgaGJNX9p7oaR5CQIZHpCY8,44315
28
+ qnty/problems/validation.py,sha256=SmFEsgHx5XwRNlR2suOhxO-WNsOwPZhCP8wyVKYo1EE,4826
29
+ qnty/quantities/__init__.py,sha256=K_h5v6X6-OyITSXOhbIZTDAJe6-y_7iMMDEIQ4O3luc,809
30
+ qnty/quantities/base_qnty.py,sha256=QasOR4-a7gwPBvc6cLJ3ooQHmOcWbYexgtNQ9I6bXI8,32516
31
+ qnty/quantities/field_converters.py,sha256=rDWttIE0lwF1doGlLG5RJTcTikYEYruMrRBMlr8fvBI,1008701
32
+ qnty/quantities/field_qnty.py,sha256=9A-KP8DyO5oOfmxw41HKJq48dUF0LP9M_YYqvdbVvRM,42562
33
+ qnty/quantities/field_setter.py,sha256=JCvRom4qvCYvgRNgFLZEuwb1PCmz0qrKzxDv0-h4RMo,449348
34
+ qnty/quantities/field_vars.py,sha256=mo-kh3WFx6h_dfROUIXAyhdDAoEDEgGN_TlaLwu_o1U,264561
35
+ qnty/quantities/field_vars.pyi,sha256=DJtLmxXJ9MrphAqSSOkMYNlLrq7-mAzvclp6bufT4RY,154868
36
+ qnty/solving/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ qnty/solving/manager.py,sha256=LQBMhWD3ajRYMBXkwRpkVzdo7qVEDviBAoHpjAzS-0U,3945
38
+ qnty/solving/order.py,sha256=q1G3fMWamhmBK6vN0L2BuTqWl0aa94ZJPCWusrntcXo,15488
39
+ qnty/solving/solvers/__init__.py,sha256=7hM2Fz4jCS8xf5Kd3qDqkEmvtT1PO72jXguoQA_Fvqo,475
40
+ qnty/solving/solvers/base.py,sha256=UgXRhnx9m331Hn2P3U-0qTfSQIkfOhEw_MfqcLhxxBM,2839
41
+ qnty/solving/solvers/iterative.py,sha256=dZw66VRz0ScnkPsJl2miKgH7VVcGJQw1zq-5wzU0CZU,7557
42
+ qnty/solving/solvers/simultaneous.py,sha256=hLZ8878BqiI5NQd8eeRG5Sy9ztS43BBX4mk3BDMVs_4,21609
43
+ qnty/units/__init__.py,sha256=WgH6t1obC5i2ioEykO8H3z_csoRL6fP6xjzXn2_EB80,27
44
+ qnty/units/field_units.py,sha256=juW8qzt7UaL4lIMl5p_GVcfH6h6gQz7bFuOUOKRX7rY,283034
45
+ qnty/units/field_units.pyi,sha256=1iW_yWF2PWQ8wZ1vSp2SbHk4A6eiG-Hfw-9VdSBf7s0,77949
46
+ qnty/units/prefixes.py,sha256=tSD8CIPjFtCuwl2Og1a1pmqLoTvfeTljxMqPuHBC_dE,6667
47
+ qnty/units/registry.py,sha256=bfnD4kWkaEnX0Vkb7Dxa1K-YGhtjMNQjevLpcMc7KCQ,6728
48
+ qnty/utils/__init__.py,sha256=r9sYK7anOF5KoDqw6rK0O1dCVXV4MKAtt_4gab7PZIk,533
49
+ qnty/utils/caching/__init__.py,sha256=vgR18cUhDnOkB-3Qv-Kcj0l9lSZ_cD48MSfjaAHKjpA,456
50
+ qnty/utils/caching/manager.py,sha256=B9-p9eD57SjBTZwfa8LqfAwK7arhy0zH7faoE_lQt8M,16893
51
+ qnty/utils/error_handling/__init__.py,sha256=6J8a1NdaeQGtGa6r6Rc-x8FcIc5adJZ6ngm21bZXbFI,1571
52
+ qnty/utils/error_handling/context.py,sha256=hTwQst5sFWKo2UrpH6768ERwqcFfbwVQz8aNuO0fveo,1353
53
+ qnty/utils/error_handling/exceptions.py,sha256=8O0gzoTJqz1Gw9h8sdN16DKIf_ZBn2Lzsu9gyLHknXU,3608
54
+ qnty/utils/error_handling/handlers.py,sha256=_q12co-jr4YSktRoCPpGBbh6WXEDw9MbmWxUge0YWU8,8007
55
+ qnty/utils/logging.py,sha256=2H6_gSOQjxdK5024XTY3E1jGIQPE8WdalVhVBFw51OA,1143
56
+ qnty/utils/protocols.py,sha256=c_Ya_epCm7qenAADRMZiwiQ0PdD-Z4T85b1z1YQNXAk,5247
57
+ qnty/utils/scope_discovery.py,sha256=mQc-FHJ5-VNBzqQwiFofV-hqeF3GpLRaLlTjYDRnOqs,15184
58
+ qnty-0.1.0.dist-info/METADATA,sha256=IEWbkj_Ll1dkIkr50avhKUlChVE6rrAnSy5Bv9y4Ma0,6761
59
+ qnty-0.1.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
60
+ qnty-0.1.0.dist-info/RECORD,,