pydasa 0.4.7__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 (58) hide show
  1. pydasa/__init__.py +103 -0
  2. pydasa/_version.py +6 -0
  3. pydasa/analysis/__init__.py +0 -0
  4. pydasa/analysis/scenario.py +584 -0
  5. pydasa/analysis/simulation.py +1158 -0
  6. pydasa/context/__init__.py +0 -0
  7. pydasa/context/conversion.py +11 -0
  8. pydasa/context/system.py +17 -0
  9. pydasa/context/units.py +15 -0
  10. pydasa/core/__init__.py +15 -0
  11. pydasa/core/basic.py +287 -0
  12. pydasa/core/cfg/default.json +136 -0
  13. pydasa/core/constants.py +27 -0
  14. pydasa/core/io.py +102 -0
  15. pydasa/core/setup.py +269 -0
  16. pydasa/dimensional/__init__.py +0 -0
  17. pydasa/dimensional/buckingham.py +728 -0
  18. pydasa/dimensional/fundamental.py +146 -0
  19. pydasa/dimensional/model.py +1077 -0
  20. pydasa/dimensional/vaschy.py +633 -0
  21. pydasa/elements/__init__.py +19 -0
  22. pydasa/elements/parameter.py +218 -0
  23. pydasa/elements/specs/__init__.py +22 -0
  24. pydasa/elements/specs/conceptual.py +161 -0
  25. pydasa/elements/specs/numerical.py +469 -0
  26. pydasa/elements/specs/statistical.py +229 -0
  27. pydasa/elements/specs/symbolic.py +394 -0
  28. pydasa/serialization/__init__.py +27 -0
  29. pydasa/serialization/parser.py +133 -0
  30. pydasa/structs/__init__.py +0 -0
  31. pydasa/structs/lists/__init__.py +0 -0
  32. pydasa/structs/lists/arlt.py +578 -0
  33. pydasa/structs/lists/dllt.py +18 -0
  34. pydasa/structs/lists/ndlt.py +262 -0
  35. pydasa/structs/lists/sllt.py +746 -0
  36. pydasa/structs/tables/__init__.py +0 -0
  37. pydasa/structs/tables/htme.py +182 -0
  38. pydasa/structs/tables/scht.py +774 -0
  39. pydasa/structs/tools/__init__.py +0 -0
  40. pydasa/structs/tools/hashing.py +53 -0
  41. pydasa/structs/tools/math.py +149 -0
  42. pydasa/structs/tools/memory.py +54 -0
  43. pydasa/structs/types/__init__.py +0 -0
  44. pydasa/structs/types/functions.py +131 -0
  45. pydasa/structs/types/generics.py +54 -0
  46. pydasa/validations/__init__.py +0 -0
  47. pydasa/validations/decorators.py +510 -0
  48. pydasa/validations/error.py +100 -0
  49. pydasa/validations/patterns.py +32 -0
  50. pydasa/workflows/__init__.py +1 -0
  51. pydasa/workflows/influence.py +497 -0
  52. pydasa/workflows/phenomena.py +529 -0
  53. pydasa/workflows/practical.py +765 -0
  54. pydasa-0.4.7.dist-info/METADATA +320 -0
  55. pydasa-0.4.7.dist-info/RECORD +58 -0
  56. pydasa-0.4.7.dist-info/WHEEL +5 -0
  57. pydasa-0.4.7.dist-info/licenses/LICENSE +674 -0
  58. pydasa-0.4.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,394 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Module symbolic.py
4
+ ===========================================
5
+
6
+ Symbolic perspective for variable representation.
7
+
8
+ This module defines the SymbolicSpecs class representing the mathematical
9
+ and notational properties of a variable.
10
+
11
+ Classes:
12
+ **SymbolicSpecs**: Symbolic variable specifications
13
+
14
+ *IMPORTANT:* Based on the theory from:
15
+
16
+ # H.Gorter, *Dimensionalanalyse: Eine Theoririe der physikalischen Dimensionen mit Anwendungen*
17
+ """
18
+
19
+ # native python modules
20
+ # dataclass imports
21
+ from __future__ import annotations
22
+ from dataclasses import dataclass, field
23
+ from typing import Optional, List, TYPE_CHECKING
24
+ import re
25
+
26
+ # custom modules
27
+ from pydasa.validations.decorators import validate_type
28
+ from pydasa.validations.decorators import validate_emptiness
29
+
30
+ # Type checking imports (not at runtime to avoid circular imports)
31
+ if TYPE_CHECKING:
32
+ from pydasa.dimensional.vaschy import Schema
33
+
34
+
35
+ @dataclass
36
+ class SymbolicSpecs:
37
+ """Symbolic perspective: mathematical representation. Answers the question: "How do we WRITE this variable?"
38
+
39
+ This perspective focuses on:
40
+ - Symbol notation (LaTeX, Python alias)
41
+ - Dimensional formulas (L, M, T, etc.)
42
+ - Unit systems (original and standardized)
43
+ - Matrix representation for linear algebra
44
+ - Integration with symbolic math libraries
45
+
46
+ Attributes:
47
+ # From SymbolicSpecs
48
+ _dims (str): Dimensional expression (e.g., "L*T^-1").
49
+ _units (str): Units of measure (e.g., "m/s").
50
+ _std_dims (Optional[str]): Standardized dimensional expression. e.g.: from [T^2*L^-1] to [L^(-1)*T^(2)].
51
+ _sym_exp (Optional[str]): Sympy-compatible dimensional expression. e.g.: from [T^2*L^-1] to [T**2*L**(-1)].
52
+ _dim_col (List[int]): Dimensional column for matrix operations. e.g.: from [T^2*L^-1] to [2, -1].
53
+ _std_units (str): Standardized units of measure. e.g `km/h` -> `m/s`, `kByte/s` -> `bit/s`.
54
+ """
55
+
56
+ # Type annotation for _schema attribute (defined in ConceptualSpecs, accessed via composition)
57
+ if TYPE_CHECKING:
58
+ _schema: Optional[Schema]
59
+
60
+ # Dimensional properties
61
+ # :attr: _dims
62
+ _dims: str = ""
63
+ """Dimensional expression (e.g., "L*T^-1")."""
64
+
65
+ # :attr: _units
66
+ _units: str = ""
67
+ """Units of measure (e.g., "m/s")."""
68
+
69
+ # Processed dimensional attributes
70
+ # :attr: _std_dims
71
+ _std_dims: Optional[str] = None
72
+ """Standardized dimensional expression. e.g.: from [T^2*L^-1] to [L^(-1)*T^(2)]."""
73
+
74
+ # :attr: _sym_exp
75
+ _sym_exp: Optional[str] = None
76
+ """Sympy-compatible dimensional expression. e.g.: from [T^2*L^-1] to [T**2*L**(-1)]."""
77
+
78
+ # :attr: _std_col
79
+ _dim_col: List[int] = field(default_factory=list)
80
+ """Dimensional column for matrix operations. e.g.: from [T^2*L^-1] to [2, -1]."""
81
+
82
+ # Value ranges (standardized units)
83
+ # :attr: _std_units
84
+ _std_units: str = ""
85
+ """Standardized units of measure. e.g `km/h` -> `m/s`, `kByte/s` -> `bit/s`."""
86
+
87
+ def _validate_exp(self, exp: str, regex: str) -> bool:
88
+ """*_validate_exp()* Validates an expression using a regex pattern (inclde dimensions and units,).
89
+
90
+ Args:
91
+ dims (str): Dimensions of the parameter. It is a string with the FDU formula of the parameter. e.g.: [T^2*L^-1]
92
+
93
+ Returns:
94
+ bool: True if the dimensions are valid, False otherwise, ignoring null or empty strings.
95
+ """
96
+ # TODO improve this ignoring null or empty strings for constants
97
+ if exp in [None, ""]:
98
+ return True
99
+ return bool(re.match(regex, exp))
100
+
101
+ def _prepare_dims(self) -> None:
102
+ """*_prepare_dims()* Processes dimensional expressions for analysis.
103
+
104
+ Standardizes and sorts dimensions, creates sympy expression and dimensional column.
105
+ """
106
+ self._std_dims = self._standardize_dims(self._dims)
107
+ self._std_dims = self._sort_dims(self._std_dims)
108
+ self._sym_exp = self._setup_sympy(self._std_dims)
109
+ self._dim_col = self._setup_column(self._sym_exp)
110
+
111
+ def _standardize_dims(self, dims: str) -> str:
112
+ """*_standardize_dims()* Standardizes dimensional expression format.
113
+
114
+ Args:
115
+ dims (str): Dimensional expression (e.g., "L*T^-1").
116
+
117
+ Returns:
118
+ str: Standardized expression with parentheses (e.g., "L^(1)*T^(-1)").
119
+ """
120
+ if self._schema is None:
121
+ _msg = "Schema must be initialized before standardizing dimensions"
122
+ raise ValueError(_msg)
123
+
124
+ # Add parentheses to powers
125
+ # _pattern = re.compile(cfg.WKNG_POW_RE)
126
+ _pattern = re.compile(self._schema.fdu_pow_regex)
127
+ dims = _pattern.sub(lambda m: f"({m.group(0)})", dims)
128
+
129
+ # Add ^1 to dimensions without explicit powers
130
+ # _pattern = re.compile(cfg.WKNG_NO_POW_RE)
131
+ _pattern = re.compile(self._schema.fdu_no_pow_regex)
132
+ dims = _pattern.sub(lambda m: f"{m.group(0)}^(1)", dims)
133
+ return dims
134
+
135
+ def _sort_dims(self, dims: str) -> str:
136
+ """*_sort_dims()* Sorts dimensions based on FDU precedence.
137
+
138
+ Args:
139
+ dims (str): Standardized dimensional expression. e.g.: [T^2*L^-1].
140
+
141
+ Returns:
142
+ str: Sorted dimensional expression. e.g.: [L^(-1)*T^(2)].
143
+ """
144
+ if self._schema is None:
145
+ _msg = "Schema must be initialized before standardizing dimensions"
146
+ raise ValueError(_msg)
147
+ # Local variable for type narrowing
148
+ _schema = self._schema
149
+
150
+ # TODO move '*' as global operator to cfg module?
151
+ # Split by multiplication operator
152
+ _dims_lt = dims.split("*")
153
+
154
+ # Sort based on FDU precedence
155
+ # _dims_lt.sort(key=lambda x: cfg.WKNG_FDU_PREC_LT.index(x[0]))
156
+ _dims_lt.sort(key=lambda x: _schema.fdu_symbols.index(x[0]))
157
+
158
+ # Rejoin with multiplication operator
159
+ _dims = "*".join(_dims_lt)
160
+ return _dims
161
+
162
+ def _setup_sympy(self, dims: str) -> str:
163
+ """*_setup_sympy()* Creates sympy-compatible expression.
164
+
165
+ Args:
166
+ dims (str): Standardized dimensional expression. e.g.: [T^2*L^-1].
167
+
168
+ Returns:
169
+ str: Sympy-compatible expression. e.g.: [T**2* L**(-1)].
170
+ """
171
+ # TODO move '*' and '* ' as global operator to cfg module?
172
+ # TODO do I use also regex for this?
173
+ # replace '*' with '* ' for sympy processing
174
+ # # replace '^' with '**' for sympy processing
175
+ return dims.replace("*", "* ").replace("^", "**")
176
+
177
+ def _setup_column(self, dims: str) -> List[int]:
178
+ """*_setup_column()* Generates dimensional column (list of exponents) in the Dimensional Matrix.
179
+
180
+ Args:
181
+ dims (str): Standardized dimensional expression. e.g.: [T^(2)*L^(-1)]
182
+
183
+ Returns:
184
+ List[int]: Exponents with the dimensional expression. e.g.: [2, -1]
185
+
186
+ Raises:
187
+ ValueError: If dimensional expression cannot be parsed.
188
+ """
189
+ if self._schema is None:
190
+ _msg = "Schema must be initialized before standardizing dimensions"
191
+ raise ValueError(_msg)
192
+ # Local variable for type narrowing
193
+ _schema = self._schema
194
+
195
+ # split the sympy expression into a list of dimensions
196
+ dims_list = dims.split("* ")
197
+ # set the default list of zeros with the FDU length
198
+ # col = [0] * len(cfg.WKNG_FDU_PREC_LT)
199
+ col = [0] * len(_schema.fdu_symbols)
200
+
201
+ for dim in dims_list:
202
+ # match the exponent of the dimension
203
+ exp_match = re.search(_schema.fdu_pow_regex, dim)
204
+ if exp_match is None:
205
+ _msg = f"Could not extract exponent from dimension: {dim}"
206
+ raise ValueError(_msg)
207
+ _exp = int(exp_match.group(0))
208
+
209
+ # match the symbol of the dimension
210
+ sym_match = re.search(_schema.fdu_sym_regex, dim)
211
+ if sym_match is None:
212
+ _msg = f"Could not extract symbol from dimension: {dim}"
213
+ raise ValueError(_msg)
214
+ _sym = sym_match.group(0)
215
+
216
+ # Check if symbol exists in the precedence list
217
+ if _sym not in _schema.fdu_symbols:
218
+ _msg = f"Unknown dimensional symbol: {_sym}"
219
+ raise ValueError(_msg)
220
+
221
+ # update the column with the exponent of the dimension
222
+ col[_schema.fdu_symbols.index(_sym)] = _exp
223
+
224
+ return col
225
+
226
+ # Dimensional Properties
227
+
228
+ @property
229
+ def dims(self) -> str:
230
+ """*dims* Get the dimensional expression.
231
+
232
+ Returns:
233
+ str: Dimensions. e.g.: [T^2*L^-1]
234
+ """
235
+ return self._dims
236
+
237
+ @dims.setter
238
+ @validate_type(str, allow_none=False)
239
+ @validate_emptiness()
240
+ def dims(self, val: str) -> None:
241
+ """*dims* Sets the dimensional expression.
242
+
243
+ Args:
244
+ val (str): Dimensions. e.g.: [T^2*L^-1]
245
+
246
+ Raises:
247
+ ValueError: If expression is empty
248
+ ValueError: If dimensions are invalid according to the precedence.
249
+ """
250
+ if self._schema is None:
251
+ _msg = "Schema must be initialized before standardizing dimensions"
252
+ raise ValueError(_msg)
253
+ # Local variable for type narrowing
254
+ _schema = self._schema
255
+
256
+ # Process dimensions
257
+ if val and not self._validate_exp(val, _schema.fdu_regex):
258
+ _msg = f"Invalid dimensional expression: {val}. "
259
+ _msg += f"FDUS precedence is: {_schema.fdu_regex}"
260
+ raise ValueError(_msg)
261
+
262
+ self._dims = val
263
+
264
+ # automatically prepare the dimensions for analysis
265
+ self._prepare_dims()
266
+
267
+ @property
268
+ def units(self) -> str:
269
+ """*units* Get the units of measure.
270
+
271
+ Returns:
272
+ str: Units of measure. e.g.: `m/s`, `kg/m3`, etc.
273
+ """
274
+ return self._units
275
+
276
+ @units.setter
277
+ @validate_type(str, allow_none=False)
278
+ @validate_emptiness()
279
+ def units(self, val: str) -> None:
280
+ """*units* Sets the units of measure.
281
+
282
+ Args:
283
+ val (str): Units of measure. i.e `m/s`, `kg/m3`, etc.
284
+
285
+ Raises:
286
+ ValueError: If units are empty.
287
+ """
288
+ self._units = val
289
+
290
+ @property
291
+ def sym_exp(self) -> Optional[str]:
292
+ """*sym_exp* Get Sympy-compatible expression.
293
+
294
+ Returns:
295
+ Optional[str]: Sympy expression. e.g.: [T**2*L**(-1)]
296
+ """
297
+ return self._sym_exp
298
+
299
+ @sym_exp.setter
300
+ @validate_type(str, allow_none=False)
301
+ @validate_emptiness()
302
+ def sym_exp(self, val: str) -> None:
303
+ """*sym_exp* Sets Sympy-compatible expression.
304
+
305
+ Args:
306
+ val (str): Sympy expression. e.g.: [T**2*L**(-1)]
307
+
308
+ Raises:
309
+ ValueError: If the string is empty.
310
+ """
311
+ self._sym_exp = val
312
+
313
+ @property
314
+ def dim_col(self) -> Optional[List[int]]:
315
+ """*dim_col* Get dimensional column.
316
+
317
+ Returns:
318
+ Optional[List[int]]: Dimensional exponents. e.g.: [2, -1]
319
+ """
320
+ return self._dim_col
321
+
322
+ @dim_col.setter
323
+ def dim_col(self, val: List[int]) -> None:
324
+ """*dim_col* Sets the dimensional column
325
+
326
+ Args:
327
+ val (List[int]): Dimensional exponents. i.e..: [2, -1]
328
+
329
+ Raises:
330
+ ValueError: if the dimensional column is not a list of integers.
331
+ """
332
+ if val is not None and not isinstance(val, list):
333
+ raise ValueError("Dimensional column must be a list of integers.")
334
+ self._dim_col = val
335
+
336
+ # Standardized Dimensional Properties
337
+
338
+ @property
339
+ def std_dims(self) -> Optional[str]:
340
+ """*std_dims* Get the standardized dimensional expression.
341
+
342
+ Returns:
343
+ Optional[str]: Standardized dimensional expression. e.g.: [L^(-1)*T^(2)]
344
+ """
345
+ return self._std_dims
346
+
347
+ @std_dims.setter
348
+ @validate_type(str, allow_none=False)
349
+ @validate_emptiness()
350
+ def std_dims(self, val: str) -> None:
351
+ """*std_dims* Sets the standardized dimensional expression.
352
+
353
+ Args:
354
+ val (str): Standardized dimensional expression. e.g.: [L^(-1)*T^(2)]
355
+
356
+ Raises:
357
+ ValueError: If the standardized dimensional expression is empty.
358
+ """
359
+ self._std_dims = val
360
+
361
+ @property
362
+ def std_units(self) -> Optional[str]:
363
+ """*std_units* Get the standardized Unit of Measure.
364
+
365
+ Returns:
366
+ Optional[str]: standardized Unit of Measure.
367
+ """
368
+ return self._std_units
369
+
370
+ @std_units.setter
371
+ @validate_type(str, allow_none=False)
372
+ @validate_emptiness()
373
+ def std_units(self, val: str) -> None:
374
+ """*std_units* Sets the standardized Unit of Measure.
375
+
376
+ Args:
377
+ val (Optional[str]): standardized Unit of Measure.
378
+
379
+ Raises:
380
+ ValueError: If standardized units are empty.
381
+ """
382
+ self._std_units = val
383
+
384
+ def clear(self) -> None:
385
+ """*clear()* Reset symbolic attributes to default values.
386
+
387
+ Resets dimensions, units, and all processed dimensional attributes.
388
+ """
389
+ self._dims = ""
390
+ self._units = ""
391
+ self._std_dims = None
392
+ self._sym_exp = None
393
+ self._dim_col = []
394
+ self._std_units = ""
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Module serialization
4
+ ===========================================
5
+
6
+ Serialization and parsing utilities for PyDASA.
7
+
8
+ This module provides utilities for:
9
+ - LaTeX expression parsing and conversion
10
+ - Symbol mapping and transformation
11
+ - Future: JSON, YAML serialization support
12
+
13
+ Modules:
14
+ **parser**: LaTeX parsing and Python conversion utilities
15
+ """
16
+
17
+ from pydasa.serialization.parser import (
18
+ latex_to_python,
19
+ parse_latex,
20
+ create_latex_mapping,
21
+ )
22
+
23
+ __all__ = [
24
+ "latex_to_python",
25
+ "parse_latex",
26
+ "create_latex_mapping",
27
+ ]
@@ -0,0 +1,133 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Module latex.py
4
+ ===========================================
5
+
6
+ Module for default global variables and comparison functions for use by all *PyDASA* and its Data Structs.
7
+ """
8
+ # python native modules
9
+ # from dataclasses import dataclass
10
+ # from typing import TypeVar
11
+ from typing import Tuple, Dict
12
+ import re
13
+
14
+ # custom modules
15
+ from sympy.parsing.latex import parse_latex
16
+ from sympy import Symbol, symbols
17
+
18
+ # import global variables
19
+ from pydasa.validations.patterns import LATEX_RE
20
+
21
+ # Global vars for special Latex symbos and functions to ignore
22
+ IGNORE_EXPR = {
23
+ "\\frac",
24
+ "\\sqrt",
25
+ "\\sin",
26
+ "\\cos",
27
+ "\\tan",
28
+ "\\log",
29
+ "\\exp"
30
+ }
31
+
32
+
33
+ # Latex Parsing Functions
34
+
35
+ def latex_to_python(expr: str) -> str:
36
+ """*latex_to_python()* Convert a LaTeX expression to a Python-compatible string.
37
+
38
+ Args:
39
+ expr (str): The LaTeX expression to convert.
40
+
41
+ Returns:
42
+ str: The Python-compatible string.
43
+ """
44
+ # Replace LaTeX subscript with Python style
45
+ if expr.isalnum():
46
+ return expr
47
+ # TODO this regex doesnt work, check latter
48
+ # ans = re.sub(r"\\([a-zA-Z]+)_{(\d+)}", r"\1_\2", expr)
49
+ alias = expr.replace("\\", "")
50
+ alias = alias.replace("_{", "_").replace("}", "")
51
+ return alias
52
+
53
+
54
+ def extract_latex_vars(expr: str) -> Tuple[Dict[str, str], Dict[str, str]]:
55
+ """*extract_latex_vars()* Extract variable names in LaTeX format with their Python equivalents.
56
+
57
+ Args:
58
+ expr (str): The LaTeX expression to parse.
59
+
60
+ Returns:
61
+ tuple [dict]: A tuple containing two dictionaries:
62
+ - The first dictionary maps LaTeX variable names to their Python equivalents.
63
+ - The second dictionary maps Python variable names to their LaTeX equivalents.
64
+ """
65
+ # Extract latex variable names with regex
66
+ matches = re.findall(LATEX_RE, expr)
67
+
68
+ # Filter out ignored LaTeX commands
69
+ matches = [m for m in matches if m not in IGNORE_EXPR]
70
+
71
+ # Create mappings both ways
72
+ latex_to_py = {}
73
+ py_to_latex = {}
74
+
75
+ for m in matches:
76
+ # Keep original LaTeX notation for external reference
77
+ latex_var = m
78
+ # Convert to Python style for internal use
79
+ py_var = m.lstrip("\\")
80
+ py_var = py_var.replace("_{", "_")
81
+ py_var = py_var.replace("}", "")
82
+
83
+ latex_to_py[latex_var] = py_var
84
+ py_to_latex[py_var] = latex_var
85
+
86
+ return latex_to_py, py_to_latex
87
+
88
+
89
+ def create_latex_mapping(expr: str) -> Tuple[Dict[Symbol, Symbol], # symbol_map
90
+ Dict[str, Symbol], # py_symbol_map
91
+ Dict[str, str], # latex_to_py
92
+ Dict[str, str] # py_to_latex
93
+ ]:
94
+ """*create_latex_mapping()* Create a mapping between LaTeX symbols and Python symbols.
95
+
96
+ Args:
97
+ expr (str): The LaTeX expression to parse.
98
+
99
+ Returns:
100
+ tuple[dict]: A tuple containing:
101
+ - A dictionary mapping LaTeX symbols to Python symbols for internal substitution.
102
+ - A dictionary mapping Python variable names to their corresponding sympy symbols for lambdify.
103
+ - A dictionary mapping LaTeX variable names to their Python equivalents.
104
+ - A dictionary mapping Python variable names to their LaTeX equivalents.
105
+ """
106
+ # Get LaTeX<->Python variable mappings
107
+ latex_to_py, py_to_latex = extract_latex_vars(expr)
108
+
109
+ # Parse to get LaTeX symbols
110
+ sym_expr = parse_latex(expr)
111
+
112
+ # Create mapping for sympy substitution
113
+ symbol_map = {} # For internal substitution
114
+ py_symbol_map = {} # For lambdify
115
+
116
+ for latex_sym in sym_expr.free_symbols:
117
+ latex_name = str(latex_sym)
118
+
119
+ # Find corresponding Python name
120
+ for latex_var, py_var in latex_to_py.items():
121
+ # Check for various forms of equivalence
122
+ con1 = (latex_name == latex_var)
123
+ con2 = (latex_name == py_var)
124
+ con3 = (latex_name.replace("_{", "_").replace("}", "") == py_var)
125
+ if con1 or con2 or con3:
126
+ # Create symbol for this variable
127
+ sym = symbols(py_var)
128
+ # Store mappings
129
+ symbol_map[latex_sym] = sym # For substitution
130
+ py_symbol_map[py_var] = sym # For lambdify args
131
+ break
132
+
133
+ return symbol_map, py_symbol_map, latex_to_py, py_to_latex
File without changes
File without changes