<jupyter_start><jupyter_text>Backcalculate process mixesThis notebook is an attempt to figure out how easy it would be to backcalculate the inputs of an aggregated process, assuming that you knew the background database. We will use `ecoinvent 3.1 cutoff` as the background database and inventories, without applying LCIA methods.We first set up our project.<jupyter_code>from brightway2 import *
from scipy.optimize import *
import numpy as np
import pyprind
from scipy.optimize import minimize
from time import time<jupyter_output><empty_output><jupyter_text>Create new project<jupyter_code>projects.current = "backcalculate"<jupyter_output><empty_output><jupyter_text>Basic setup: biosphere, LCIA methods, etc.<jupyter_code>bw2setup()<jupyter_output>Creating default biosphere

Applying strategy: normalize_units<jupyter_text>Import ecoinvent 3.1<jupyter_code>ei31cutoff = SingleOutputEcospold2Importer(
    "/Users/cmutel/Documents/LCA Documents/Ecoinvent/3.1/cutoff/datasets",
    "ecoinvent 3.1 cutoff"
)
ei31cutoff.apply_strategies()
ei31cutoff.statistics()
ei31cutoff.write_database()<jupyter_output>Writing activities to SQLite3 database:
0%                          100%
[##############################] | ETA[sec]: 0.000 
Total time elapsed: 65.508 sec<jupyter_text>Clear import object to free memory<jupyter_code>ei31cutoff = None<jupyter_output><empty_output><jupyter_text>Calculate all product inventory vectorsWe next need to know all inventory vectors for all processes.<jupyter_code>db = Database("ecoinvent 3.1 cutoff")
lca = LCA({db.random(): 1})
lca.lci(factorize=True)
mapping, vectors = {}, []

pbar = pyprind.ProgBar(len(db), title="Datasets:")

vectors = np.zeros((len(db), lca.inventory.shape[0]))

for index, ds in enumerate(db):
    lca.redo_lci({ds: 1})
    vectors[index, :] = np.array(lca.inventory.sum(axis=1)).ravel()
    mapping[ds.key] = index
    pbar.update()<jupyter_output>Datasets:
0%                          100%
[                              ]Warning: (almost) singular matrix! (estimated cond. number: 2.20e+12)
[##############################] | ETA[sec]: 0.000 
Total time elapsed: 206.648 sec<jupyter_text>Get optimization function<jupyter_code>class ScoringFunction(object):
    """Scoring function for inventory vector.
    
    Calculate the unweighted Manhattan distance in n-dimensional space between 
    answer * vectors and guess * vectors, where n is the number of processes 
    in the database. Zeros are included and weighted the same as other values.
    
    Returns an number when called with a vector guess."""
    def __init__(self, lca, vectors):
        assert isinstance(lca, LCA) 
        assert isinstance(vectors, np.ndarray)
        assert hasattr(lca, "inventory")

        self.lca = lca
        self.vectors = vectors

    def sum_inventory_matrix(self, matrix):
        return np.array(matrix.sum(axis=1)).ravel()
        
    def set_answer(self, answer):
        """Given functional unit ``answer``, create an inventory vector of shape ``(flows,)``"""
        self.lca.redo_lci(answer)
        self.answer = self.sum_inventory_matrix(lca.inventory)
        
    def __call__(self, guess):
        """Evaluate a guess vector of shape ``(processes,)``"""
        assert guess.shape[0] == self.vectors.shape[0]
        assert len(guess.shape) == 1
        # Translate from (processes,) to (flows,)
        guess = guess.reshape((1, -1)).dot(self.vectors)
        # Get manhattan distance between two vectors
        distance = np.abs(guess - self.answer).sum()
        return distance<jupyter_output><empty_output><jupyter_text>Create an artificial system<jupyter_code>answer = {db.random().key: float(np.random.random() * 10) for _ in range(5)}<jupyter_output><empty_output><jupyter_text>Set up and check our scoring function<jupyter_code>scorer = ScoringFunction(lca, vectors)
scorer.set_answer(answer)<jupyter_output>Warning: (almost) singular matrix! (estimated cond. number: 2.20e+12)<jupyter_text>Get test guess which should be perfect<jupyter_code>perfect = np.zeros((len(db),))
for key, value in answer.items():
    perfect[mapping[key]] = value
scorer(perfect)
start = time()
result = minimize(scorer, np.ones(perfect.shape), options={'maxiter': 50, 'disp': True})
print("Took {} minutes".format((time() - start)/60))<jupyter_output>Warning: Desired error not necessarily achieved due to precision loss.
         Current function value: 156104120668565.406250
         Iterations: 0
         Function evaluations: 124345
         Gradient evaluations: 11
Took 20.973269168535868 minutes<jupyter_text>Display how close we can get<jupyter_code>def how_close(guess):
    for key, value in answer.items():
        print(get_, value, mapping[key])
ALGORITHMS = ('Nelder-Mead', 'Powell', 'CG', 'BFGS', 'L-BFGS-B', 'TNC', 'COBYLA', 'SLSQP')

for algo in ALGORITHMS:
    r = minimize(MCDA, 5500., method=algo)
    print(algo, r['x'])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12434183880113406, 'fraction_numerical': 0.02612393681652491, 'mean_word_length': 4.380174291938998, 'pattern_counts': {'":': 0, '<': 40, '<?xml version=': 0, '>': 40, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '165454', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 1693, 'n_tokens_neox': 1564, 'n_words': 405, 'path': 'notebooks/Backcalculate process mixes.ipynb', 'repo_name': 'elinlucy/brightway2', 'size': '4938'}