lca-algebraic 1.4.1.dev2__tar.gz → 1.4.1.dev4__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 (39) hide show
  1. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/PKG-INFO +2 -1
  2. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/activity.py +16 -6
  3. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/base_utils.py +17 -7
  4. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/cache.py +8 -1
  5. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/lambda_expression.py +31 -10
  6. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/lca.py +154 -106
  7. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/log.py +0 -8
  8. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/matrix.py +38 -13
  9. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/methods.py +1 -6
  10. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/params.py +36 -8
  11. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/settings.py +0 -4
  12. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/stats.py +122 -15
  13. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/sympy_utils.py +1 -3
  14. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic.egg-info/PKG-INFO +2 -1
  15. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic.egg-info/SOURCES.txt +1 -0
  16. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic.egg-info/requires.txt +1 -0
  17. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/pyproject.toml +3 -2
  18. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_premise.py +1 -33
  19. lca_algebraic-1.4.1.dev4/test/test_stats.py +86 -0
  20. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_units.py +78 -8
  21. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/LICENSE +0 -0
  22. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/README.md +0 -0
  23. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/__init__.py +0 -0
  24. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/axis_dict.py +0 -0
  25. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/bw_wrapper/__init__.py +0 -0
  26. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/bw_wrapper/bw2.py +0 -0
  27. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/bw_wrapper/bw25.py +0 -0
  28. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/bw_wrapper/protocol.py +0 -0
  29. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/database.py +0 -0
  30. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/interpolation.py +0 -0
  31. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/io.py +0 -0
  32. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/units.py +0 -0
  33. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic.egg-info/dependency_links.txt +0 -0
  34. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic.egg-info/top_level.txt +0 -0
  35. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/setup.cfg +0 -0
  36. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_axis_dict.py +0 -0
  37. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_axis_matrices_cache.py +0 -0
  38. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_cache.py +0 -0
  39. {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_sympy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lca_algebraic
3
- Version: 1.4.1.dev2
3
+ Version: 1.4.1.dev4
4
4
  Summary: This library provides a layer above Brightway for defining parametric models and running super fast LCA for monte carlo analysis.
5
5
  Author-email: OIE - Mines ParisTech <raphael.jolivet@mines-paristech.fr>
6
6
  License: BSD
@@ -41,6 +41,7 @@ Requires-Dist: pre-commit; extra == "dev"
41
41
  Requires-Dist: pytest; extra == "dev"
42
42
  Requires-Dist: nbmake; extra == "dev"
43
43
  Requires-Dist: build; extra == "dev"
44
+ Requires-Dist: twine>=6.1; extra == "dev"
44
45
  Dynamic: license-file
45
46
 
46
47
 
@@ -29,6 +29,7 @@ from lca_algebraic.database import (
29
29
  )
30
30
  from lca_algebraic.params import (
31
31
  STORE_FORMULA_KEY,
32
+ STORE_FORMULA_TAG,
32
33
  DbContext,
33
34
  ParamDef,
34
35
  _complete_and_expand_params,
@@ -318,6 +319,10 @@ class ActivityExtended(Activity):
318
319
  def _amount_to_formula(self, amount: ValueOrExpression, exchange: ExchangeDataset):
319
320
  res = dict()
320
321
  if isinstance(amount, Basic):
322
+
323
+ if amount.is_number:
324
+ return {"amount": float(amount.evalf())}
325
+
321
326
  current_amount = exchange.get("amount", None)
322
327
  if current_amount is not None:
323
328
  amount = amount.subs(old_amount, current_amount)
@@ -329,8 +334,9 @@ class ActivityExtended(Activity):
329
334
  raise Exception("Symbol '%s' not found in params : %s" % (symbol, all_symbols))
330
335
 
331
336
  res[STORE_FORMULA_KEY] = str(amount)
337
+ res[STORE_FORMULA_TAG] = True
332
338
  res["amount"] = 0
333
- elif isinstance(amount, float) or isinstance(amount, int):
339
+ elif isinstance(amount, float|int):
334
340
  res["amount"] = amount
335
341
  else:
336
342
  raise Exception(
@@ -349,6 +355,9 @@ class ActivityExtended(Activity):
349
355
  # Update units
350
356
  amount = self._transform_unit(amount, exchange["unit"])
351
357
 
358
+ updates.pop(STORE_FORMULA_TAG, None)
359
+ exchange.pop(STORE_FORMULA_TAG, None)
360
+
352
361
  # Extract formula if two separate field "amount" and "formula"
353
362
  # Update the list of updates
354
363
  updates.update(self._amount_to_formula(amount, exchange))
@@ -629,9 +638,7 @@ def newActivity(
629
638
 
630
639
 
631
640
  @atomic
632
- def copyActivity(
633
- db_name, activity: ActivityExtended, code=None, withExchanges=True, input_mapping=None, **kwargs
634
- ) -> ActivityExtended:
641
+ def copyActivity(db_name, activity: ActivityExtended, code=None, withExchanges=True, stripFormula=True, input_mapping=None, **kwargs) -> ActivityExtended:
635
642
  """Copy an activity and its exchanges into another database. You usually want to copy activities from your background to
636
643
  your foreground DB to update them, keeping your background DB clean.
637
644
 
@@ -643,7 +650,10 @@ def copyActivity(
643
650
  Source activity
644
651
  code:
645
652
  Code of the target activity. Also used as its name
646
-
653
+ withExchange:
654
+ Copy exchanges is True (default), otherwise do not copy them
655
+ stripFormula:
656
+ keep exchanges formula if True, default is False
647
657
 
648
658
  Returns
649
659
  -------
@@ -680,7 +690,7 @@ def copyActivity(
680
690
 
681
691
  # Chemical formulas might be in ecoinvent technosphere
682
692
  # We don't want them
683
- if "formula" in data:
693
+ if stripFormula and ("formula" in data) and (STORE_FORMULA_TAG not in data):
684
694
  del data["formula"]
685
695
  ExchangeDataset.create(**dict_as_exchangedataset(data))
686
696
 
@@ -3,14 +3,16 @@ from functools import cache
3
3
  from inspect import isfunction
4
4
  from typing import Dict, Iterable, Tuple, Union
5
5
 
6
+ import sympy
6
7
  import ipywidgets as widgets
7
8
  import numpy as np
8
9
  import pandas as pd
9
10
  from IPython.display import display
10
11
  from six import raise_from
11
12
  from sympy import Basic
12
- from sympy.physics.units import Quantity
13
+ from pint import Quantity
13
14
 
15
+ from lca_algebraic.settings import Settings
14
16
  from lca_algebraic.bw_wrapper import Activity, Database, is_output_exchange
15
17
 
16
18
  _user_functions = dict()
@@ -48,14 +50,22 @@ def _getDb(dbname):
48
50
  return dbs[dbname]
49
51
 
50
52
 
51
- def Max(a, b):
52
- """Max define as algrebraic forumal with 'abs' for proper computation on vectors"""
53
- return (a + b + abs(a - b)) / 2
53
+ def _MinMax(op, arg0, *args):
54
+ from lca_algebraic.units import unit_registry as u
55
+ if not Settings.units_enabled:
56
+ return op(arg0, *args)
57
+ same_unit = all(x.units == arg0.units for x in args)
58
+ if not same_unit:
59
+ raise Exception("MinMax argments must have the same units")
60
+ return u.Quantity(op(arg0.magnitude, *[x.magnitude for x in args]), arg0.units)
54
61
 
55
62
 
56
- def Min(a, b):
57
- """Max define as algrebraic forumal with 'abs' for proper computation on vectors"""
58
- return (a + b - abs(b - a)) / 2
63
+ def Max(arg0, *args):
64
+ return _MinMax(sympy.Max, arg0, *args)
65
+
66
+
67
+ def Min(arg0, *args):
68
+ return _MinMax(sympy.Min, arg0, *args)
59
69
 
60
70
 
61
71
  def _actDesc(act: Activity):
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  import time
3
+ from base64 import b32encode
3
4
  from collections.abc import MutableMapping
4
5
  from datetime import datetime
5
6
  from os import path
@@ -19,6 +20,10 @@ EXPR_CACHE = "expr"
19
20
  MAPPING_CACHE = "mapping"
20
21
 
21
22
 
23
+ def _filename_encode(s: str):
24
+ return b32encode(s.encode("utf-8")).decode("utf-8")
25
+
26
+
22
27
  # Overide the behaviour for pickling sympy.UndefineFunction
23
28
  class MyPickler(Pickler):
24
29
  def reducer_override(self, obj):
@@ -89,7 +94,9 @@ class SyncDict(MutableMapping):
89
94
  self.load()
90
95
 
91
96
  def _filename(self):
92
- return path.join(projects.dir, f"lca_algebraic_cache-{self.name}-{self.db_name}.pickle")
97
+ return path.join(
98
+ projects.dir, f"lca_algebraic_cache-{_filename_encode(self.name)}-{_filename_encode(self.db_name)}.pickle"
99
+ )
93
100
 
94
101
  # ---------- core MutableMapping ----------
95
102
  def __getitem__(self, key):
@@ -1,14 +1,14 @@
1
1
  from copy import copy
2
2
  from dataclasses import dataclass
3
- from typing import Dict, List, Optional
3
+ from typing import Dict, List
4
4
 
5
5
  from sympy import Basic, Expr, lambdify, parse_expr
6
+ from sympy.core import S
6
7
  from sympy.printing.numpy import NumPyPrinter
7
8
 
8
9
  from lca_algebraic.axis_dict import AxisDict
9
10
  from lca_algebraic.base_utils import _user_functions
10
11
  from lca_algebraic.bw_wrapper import Activity
11
- from lca_algebraic.log import warn
12
12
  from lca_algebraic.params import (
13
13
  _complete_params,
14
14
  _expand_param_names,
@@ -19,6 +19,32 @@ from lca_algebraic.settings import CSE, Settings
19
19
  from lca_algebraic.sympy_utils import find_cses, find_symbols
20
20
 
21
21
 
22
+ class NumPyPrinterExt(NumPyPrinter):
23
+ def __init__(self, settings=None):
24
+ super().__init__(settings=settings)
25
+
26
+ def _helper_piecewise(self, *args):
27
+ from sympy.logic.boolalg import ITE, simplify_logic
28
+
29
+ def print_cond(cond):
30
+ """Problem having an ITE in the cond."""
31
+ if cond.has(ITE):
32
+ return self._print(simplify_logic(cond))
33
+ else:
34
+ return self._print(cond)
35
+
36
+ return "{}({},{},{})".format(
37
+ self._module_format(self._module + ".where"),
38
+ print_cond(args[0].cond),
39
+ self._print(args[0].expr),
40
+ self._helper_piecewise(*args[1:]) if len(args) > 1 else self._print(S.NaN),
41
+ )
42
+
43
+ def _print_Piecewise(self, expr):
44
+ "Piecewise function printer"
45
+ return self._helper_piecewise(*expr.args)
46
+
47
+
22
48
  @dataclass
23
49
  class ValueContext:
24
50
  """Represents a result value, with all parameters values used in context"""
@@ -40,7 +66,7 @@ class LambdaExpr:
40
66
 
41
67
  # List of background activities, indexed in the same order as impacts[i]
42
68
  self.background_activities: List[Activity] = list()
43
- self.impacts: Optional[List[float]] = None
69
+ self.impacts: List[float] = None
44
70
 
45
71
  if isinstance(expr, dict) and not isinstance(expr, AxisDict):
46
72
  # Come from JSON serialization
@@ -124,17 +150,12 @@ class LambdaExpr:
124
150
  def with_impacts(self, impacts: Dict[Activity, float]):
125
151
  """Transforms a generic LambaExpr toa one with custom impact values"""
126
152
  if self.impacts is not None:
127
- warn("You are specializing a LambaExpression with impacts already set.")
128
-
153
+ raise Exception("You are specializing a LambaExpression with impacts already set.")
129
154
  res = copy(self)
130
155
  res.impacts = [impacts[bg_act] for bg_act in self.background_activities]
131
156
 
132
157
  return res
133
158
 
134
- def impacts_dict(self):
135
- """Returns list of impacts as dict"""
136
- return {bg_act: val for bg_act, val in zip(self.background_activities, self.impacts)}
137
-
138
159
 
139
160
  def _filter_param_values(params, expanded_param_names):
140
161
  return {key: val for key, val in params.items() if key in expanded_param_names}
@@ -172,7 +193,7 @@ class LambdWrapper:
172
193
  def _lambdify(expr: Basic, expanded_params):
173
194
  """Lambdify, handling manually the case of SymDict (for impacts by axis)"""
174
195
 
175
- printer = NumPyPrinter(
196
+ printer = NumPyPrinterExt(
176
197
  {"fully_qualified_modules": False, "inline": True, "allow_unknown_functions": True, "user_functions": dict()}
177
198
  )
178
199