Repository: AUTODIAL/AutoEIS (language: python)

Issue:
Explore replacing `pyjulia` with `PythonCall.jl` for better compatiblity with `conda`
We're using `pyjulia` for Julia-Python interactions. Currently, `pyjulia` doesn't like Python environments created using `conda` (because the Python shipped by `conda` is statically linked, which is not supported right out of the box by `pyjulia`). The problem is that most people (scientists) install Python using Anaconda (which means the Python is shipped by `conda`), so that makes it tought to streamline the installation process.

An alternative is to use `PythonCall.jl` for Julia-Python interactions. The upside is that it supports statically linked Python distributions, so it works just fine with `conda` installations. The downside (I'm not sure though) might be that the first time Julia is called might be a bit slow.


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/conda.yml b/.github/workflows/conda.yml
index ce7baa5b..741dfd5f 100644
--- a/.github/workflows/conda.yml
+++ b/.github/workflows/conda.yml
@@ -11,15 +11,6 @@ jobs:
 
     steps:
       - uses: actions/checkout@v4
-      - name: "Set up Julia"
-        uses: julia-actions/setup-julia@v1
-        with:
-          version: '1.10'
-      - name: "Cache Julia"
-        uses: julia-actions/cache@v1
-        with:
-          cache-name: 'ubuntu-latest-test-1.10-3.10'
-          cache-packages: false
       - name: "Set up Python"
         uses: conda-incubator/setup-miniconda@v3
         with:
@@ -29,7 +20,8 @@ jobs:
         run: |
             python -m pip install --upgrade pip
             pip install -e .[test]
-            python -m autoeis install
+            # Test that Julia + deps are automatically installed on first import
+            python -c "import autoeis"
       - name: "Run tests"
         run: |
           pytest -v tests/
diff --git a/.github/workflows/notebooks.yml b/.github/workflows/notebooks.yml
index 75f69556..6c497dd1 100644
--- a/.github/workflows/notebooks.yml
+++ b/.github/workflows/notebooks.yml
@@ -12,7 +12,7 @@ jobs:
       matrix:
         julia-version: ['1.10']
         python-version: ['3.10']
-        os: [ubuntu-latest]
+        os: [macos-14]
 
     steps:
       - uses: actions/checkout@v4
@@ -37,4 +37,4 @@ jobs:
             python -m autoeis install
       - name: "Run tests"
         run: |
-          pytest -v --nbmake examples/*.ipynb
+          pytest -v --nbmake --nbmake-timeout=3600 examples/*.ipynb
diff --git a/autoeis/__init__.py b/autoeis/__init__.py
index 322e974b..d373c120 100644
--- a/autoeis/__init__.py
+++ b/autoeis/__init__.py
@@ -1,11 +1,3 @@
-from .utils import get_logger  # noqa: F401
-
-log = get_logger(__name__)
-log.warning(
-    "If you use the Python distribution by Conda, the initialization "
-    "of the package may take 1-2 minutes. Be patient :)"
-)
-
 from . import core, io, metrics, parser, utils, visualization  # noqa: F401, E402
 from .core import *  # noqa: E402
 from .version import __equivalent_circuits_jl_version__, __version__  # noqa: F401, E402
diff --git a/autoeis/cli.py b/autoeis/cli.py
index bb8f4ee1..803ac777 100644
--- a/autoeis/cli.py
+++ b/autoeis/cli.py
@@ -1,6 +1,6 @@
 import click
 
-from autoeis.julia_helpers import install
+from .julia_helpers import install_backend, install_julia
 
 
 @click.group("autoeis")
@@ -9,30 +9,13 @@ def autoeis_installer(context):
     ctx = context
 
 
-@autoeis_installer.command("install", help="Install Julia dependencies for AutoEIS.")
 @click.option(
-    "-p",
-    "julia_project",
-    "--project",
+    "--ec-path",
     default=None,
     type=str,
-    help="Install in a specific Julia project (e.g., a local copy of EquivalentProject.jl).",
-    metavar="PROJECT_DIRECTORY",
-)
-@click.option("-q", "--quiet", is_flag=True, default=False, help="Disable logging.")
-@click.option(
-    "--precompile",
-    "precompile",
-    flag_value=True,
-    default=None,
-    help="Force precompilation of Julia libraries.",
+    help="Installs a local copy of EquivalentCircuits instead of the remote version.",
 )
-@click.option(
-    "--no-precompile",
-    "precompile",
-    flag_value=False,
-    default=None,
-    help="Disable precompilation.",
-)
-def install_cli(julia_project, quiet, precompile):
-    install(julia_project=julia_project, quiet=quiet, precompile=precompile)
+@autoeis_installer.command("install", help="Install Julia dependencies for AutoEIS.")
+def install_cli(ec_path):
+    install_julia()
+    install_backend(ec_path=ec_path)
diff --git a/autoeis/cli_pyjulia.py b/autoeis/cli_pyjulia.py
new file mode 100644
index 00000000..bb8f4ee1
--- /dev/null
+++ b/autoeis/cli_pyjulia.py
@@ -0,0 +1,38 @@
+import click
+
+from autoeis.julia_helpers import install
+
+
+@click.group("autoeis")
+@click.pass_context
+def autoeis_installer(context):
+    ctx = context
+
+
+@autoeis_installer.command("install", help="Install Julia dependencies for AutoEIS.")
+@click.option(
+    "-p",
+    "julia_project",
+    "--project",
+    default=None,
+    type=str,
+    help="Install in a specific Julia project (e.g., a local copy of EquivalentProject.jl).",
+    metavar="PROJECT_DIRECTORY",
+)
+@click.option("-q", "--quiet", is_flag=True, default=False, help="Disable logging.")
+@click.option(
+    "--precompile",
+    "precompile",
+    flag_value=True,
+    default=None,
+    help="Force precompilation of Julia libraries.",
+)
+@click.option(
+    "--no-precompile",
+    "precompile",
+    flag_value=False,
+    default=None,
+    help="Disable precompilation.",
+)
+def install_cli(julia_project, quiet, precompile):
+    install(julia_project=julia_project, quiet=quiet, precompile=precompile)
diff --git a/autoeis/core.py b/autoeis/core.py
index 25967279..a85ee70d 100644
--- a/autoeis/core.py
+++ b/autoeis/core.py
@@ -10,8 +10,7 @@
     generate_equivalent_circuits
     filter_implausible_circuits
     perform_bayesian_inference
-    perform_bayesian_inference_batch
-    preprocess_impedance_data 
+    preprocess_impedance_data
 
 """
 import os
@@ -31,25 +30,22 @@
 from scipy.optimize import curve_fit
 from tqdm.auto import tqdm
 
-import autoeis.julia_helpers as julia_helpers
 import autoeis.visualization as viz
-from autoeis import io, metrics, parser, utils
+from autoeis import io, julia_helpers, metrics, parser, utils
 from autoeis.models import circuit_regression, circuit_regression_wrapped  # noqa: F401
 
 # AutoEIS datasets are not small-enough that CPU is much faster than GPU
 numpyro.set_platform("cpu")
 
 # HACK: Suppress output until ECSHackWeek/impedance.py/issues/280 is fixed
-linKK = utils.suppress_output(linKK)
+linKK = utils.suppress_output_legacy(linKK)
 warnings.filterwarnings("ignore", category=Warning, module="arviz.*")
 log = utils.get_logger(__name__)
 
-# Initialize Julia runtime (need to catch ImportError for pip install to work)
-try:
-    Main = julia_helpers.init_julia()
-    ec = julia_helpers.import_backend(Main)
-except Exception:
-    pass
+# Initialize Julia runtime
+julia_helpers.ensure_julia_deps_ready()
+jl = julia_helpers.init_julia()
+ec = julia_helpers.import_backend(jl)
 
 __all__ = [
     "perform_full_analysis",
@@ -230,9 +226,7 @@ def preprocess_impedance_data(
             ohmic_resistance = compute_ohmic_resistance(Z_mask, freq_mask)
             ohmic_resistance_found = True
         except ValueError:
-            log.error(
-                "Ohmic resistance not found. Check data or increase KK threshold."
-            )
+            log.error("Ohmic resistance not found. Check data or increase KK threshold.")
             ohmic_resistance_found = False
 
         # Convert the data to a dataframe for easier manipulation
@@ -308,23 +302,19 @@ def generate_equivalent_circuits(
     ecm_generator = _generate_ecm_parallel_julia if parallel else _generate_ecm_serial
     circuits = ecm_generator(impedance, freq, iters, ec_kwargs, seed)
 
-    # Convert Parameters column to dict, e.g., (R1 = 1.0, etc.) -> {"R1": 1.0, etc.}
+    # Convert output to DataFrame with columns ("circuitstring", "Parameters")
     circuits = io.parse_ec_output(circuits)
 
     if not len(circuits):
-        log.warning("No plausible circuits found. Try increasing `iters`.")
+        log.warning("No plausible circuits found. Increase `iters` or lower `tol`.")
 
     return circuits
 
 
-def _generate_ecm_serial(impedance, freq, iters, ec_kwargs, seed):
+def _generate_ecm_serial(impedance, freq, iters, ec_kwargs, seed) -> list[str]:
     """Generates candidate circuits in serial."""
-    global Main
-    Main = julia_helpers.init_julia() if Main is None else Main
-    ec = julia_helpers.import_backend(Main)
-
     # Set random seed for reproducibility
-    Main.eval(f"import Random; Random.seed!({seed})")
+    jl.seval(f"import Random; Random.seed!({seed})")
 
     circuits = []
     for _ in tqdm(range(iters), desc="Circuit Evolution"):
@@ -333,14 +323,10 @@ def _generate_ecm_serial(impedance, freq, iters, ec_kwargs, seed):
         except Exception as e:
             log.error(f"Error generating circuit: {e}")
             continue
-        if circuit != Main.nothing:
-            circuits.append(circuit)
-
-    # Format circuits as a dataframe with columns "circuitstring" and "Parameters"
-    df = [(c.circuitstring, Main.string(c.Parameters)) for c in circuits]
-    df = pd.DataFrame(df, columns=["circuitstring", "Parameters"])
+        circuits.append(circuit)
 
-    return df
+    circuits = [str(c) for c in circuits if c is not None]
+    return circuits
 
 
 # TODO: This function is deprecated, use _generate_ecm_parallel_julia instead
@@ -349,20 +335,18 @@ def _generate_ecm_parallel_mpire(impedance, freq, iters, ec_kwargs, seed):
 
     def circuit_evolution(seed: int):
         """Closure to generate a single circuit to be used with multiprocessing."""
-        global Main
-        Main = julia_helpers.init_julia() if Main is None else Main
+        jl = julia_helpers.init_julia()
+        ec = julia_helpers.import_backend(jl)
         # Set random seed for reproducibility
-        Main.eval(f"import Random; Random.seed!({seed})")
-        ec = julia_helpers.import_backend(Main)
+        jl.seval(f"import Random; Random.seed!({seed})")
         try:
             circuit = ec.circuit_evolution(impedance, freq, **ec_kwargs)
         except Exception as e:
             log.error(f"Error generating circuit: {e}")
             return None
-        if circuit == Main.nothing:
-            return None
-        # Format output as list of strings since Julia objects cannot be pickled
-        return [circuit.circuitstring, Main.string(circuit.Parameters)]
+        # # Format output as list of strings since Julia objects cannot be pickled
+        # return [circuit.circuitstring, jl.string(circuit.Parameters)]
+        return circuit
 
     nproc = os.cpu_count()
     mpire_kwargs = {
@@ -385,29 +369,17 @@ def circuit_evolution(seed: int):
             runtime_error = True
 
     if runtime_error:
-        raise RuntimeError(
-            "Julia must not be manually initialized, restart the kernel."
-        )
-
-    # Remove None values
-    circuits = [circuit for circuit in circuits if circuit is not None]
+        raise RuntimeError("Julia must not be manually initialized, restart the kernel.")
 
-    # Format circuits as a dataframe with columns "circuitstring" and "Parameters"
-    df = pd.DataFrame(circuits, columns=["circuitstring", "Parameters"])
-
-    return df
+    circuits = [str(c) for c in circuits if c is not None]
+    return circuits
 
 
 def _generate_ecm_parallel_julia(impedance, freq, iters, ec_kwargs, seed):
     """Generates candidate circuits in parallel using Julia multiprocessing."""
-    global Main
-    Main = julia_helpers.init_julia() if Main is None else Main
     # Set random seed for reproducibility (Python and Julia)
     # FIXME: This doesn't work when multiprocessing, use @everywhere instead
-    Main.eval(f"import Random; Random.seed!({seed})")
-    Main.eval("import Logging; Logging.disable_logging(Logging.Warn)")
-
-    ec = julia_helpers.import_backend(Main)
+    jl.seval(f"import Random; Random.seed!({seed})")
 
     # HACK: To get a progress bar, chunk the iterations -> call Julia repeatedly
     nprocs = psutil.cpu_count(logical=False)
@@ -434,16 +406,8 @@ def _generate_ecm_parallel_julia(impedance, freq, iters, ec_kwargs, seed):
             circuits += circuits_
             pbar.update(iters_)
 
-    # Format output as list of strings since Julia objects cannot be pickled
-    circuits_py = []
-    for circuit in circuits:
-        if circuit != Main.nothing:
-            circuits_py.append([circuit.circuitstring, Main.string(circuit.Parameters)])
-
-    # Format circuits as a dataframe with columns "circuitstring" and "Parameters"
-    df = pd.DataFrame(circuits_py, columns=["circuitstring", "Parameters"])
-
-    return df
+    circuits = [str(c) for c in circuits if c is not None]
+    return circuits
 
 
 def split_components(circuits: pd.DataFrame) -> pd.DataFrame:
diff --git a/autoeis/io.py b/autoeis/io.py
index 308e8339..482b94b1 100644
--- a/autoeis/io.py
+++ b/autoeis/io.py
@@ -13,6 +13,7 @@
 
 """
 import os
+import re
 from pathlib import Path
 
 import numpy as np
@@ -51,21 +52,20 @@ def load_test_circuits(filtered=False) -> pd.DataFrame:
     return circuits
 
 
-def parse_ec_output(circuits: pd.DataFrame) -> pd.DataFrame:
-    """
-    Parses the output of EquivalentCircuits.jl's ``circuit_evolution``
-    and returns a dataframe with circuit parameters stored as dict[str, float].
-    """
-    circuits = circuits.copy()
+def parse_ec_output(circuits: list[str]) -> list[tuple[str, dict[str, float]]]:
+    """Parses the output of EquivalentCircuits.jl's ``circuit_evolution``."""
+    # Example: 'EquivalentCircuit("R1", (R1 = 1.0,))' -> ('R1', {'R1': 1.0})
+    circuits = [circuits] if isinstance(circuits, str) else circuits
+    parsed = []
 
-    for row in circuits.itertuples():
-        pstr = row.Parameters
-        # Remove parentheses and spaces, then split by comma -> list[var=val, ...]
-        pstr = pstr.strip("()").replace(" ", "")
-        pstr = pstr.split(",")
-        # Extract variable names and values into a dictionary
-        pdict = {pair.split("=")[0]: float(pair.split("=")[1]) for pair in pstr}
-        # Replace the stringified list with a proper dict
-        circuits.at[row.Index, "Parameters"] = pdict
+    for circuit in circuits:
+        circuit = circuit.removeprefix("EquivalentCircuit(").removesuffix(")")
+        cstr = re.findall(r"\"(.*?)\"", circuit)[0]
+        pstr = re.findall(r"\((.*?)\)", circuit)[0]
+        # NOTE: rstrip(",") to account for the trailing comma in one-element tuples
+        pstr = pstr.replace(" ", "").rstrip(",").split(",")
+        pdict = dict(pair.split("=") for pair in pstr)
+        pdict = {p.split("=")[0]: float(p.split("=")[1]) for p in pstr}
+        parsed.append([cstr, pdict])
 
-    return circuits
+    return pd.DataFrame(parsed, columns=["circuitstring", "Parameters"])
diff --git a/autoeis/julia_helpers.py b/autoeis/julia_helpers.py
index cb426791..442b9488 100644
--- a/autoeis/julia_helpers.py
+++ b/autoeis/julia_helpers.py
@@ -1,368 +1,99 @@
-# This code is based on the file julia_helpers.py from the PySR project.
-# It has been adapted to suit the requirements of AutoEIS.
-# The original file is under the Apache 2.0 license.
-# Acknowledge the original authors and license when utilizing this code.
-# Original repository: https://github.com/MilesCranmer/PySR
-# Commit reference: 976f8d8 dated 2023-09-16.
+import juliapkg
+from juliapkg.find_julia import ju_find_julia_noinstall
 
-import importlib
-import os
-import subprocess
-import sys
-from pathlib import Path
+from .utils import get_logger, suppress_output
+from .version import __equivalent_circuits_jl_version__
 
-from julia.api import JuliaError
+log = get_logger(__name__)
 
-import autoeis.utils as utils
 
-from .version import __equivalent_circuits_jl_version__, __version__
+def install_julia():
+    """Installs Julia using juliapkg."""
+    # Importing juliacall automatically installs Julia using juliapkg
+    import juliacall
 
-MAX_RETRIES = 2
-juliainfo = None
-julia_initialized = False
-julia_kwargs_at_initialization = None
-julia_activated_env = None
 
-log = utils.get_logger(__name__)
-# TODO: For virtualenvs see https://github.com/JuliaPy/PyCall.jl?tab=readme-ov-file#python-virtual-environments
+def install_backend(ec_path=None):
+    """Installs Julia dependencies for AutoEIS."""
+    is_julia_installed(error=True)
 
-
-def install(
-    julia_project=None, quiet=False, precompile=None, offline=False
-):  # pragma: no cover
-    """Install all required dependencies for EquivalentCircuits.jl."""
-    import julia
-
-    _julia_version_assertion()
-    # Set JULIA_PROJECT so that we install in the autoeis environment
-    processed_julia_project, is_shared = _process_julia_project(julia_project)
-    _set_julia_project_env(processed_julia_project, is_shared)
-
-    if precompile is False:
-        os.environ["JULIA_PKG_PRECOMPILE_AUTO"] = "0"
-
-    if offline:
-        os.environ["JULIA_PKG_OFFLINE"] = "true"
-
-    try:
-        julia.install(quiet=quiet)
-    except julia.tools.PyCallInstallError:
-        # Attempt to reset PyCall.jl's build:
-        subprocess.run(
-            [
-                "julia",
-                "-e",
-                f'ENV["PYTHON"] = "{sys.executable}"; import Pkg; Pkg.build("PyCall")',
-            ],
-        )
-        # Try installing again:
-        julia.install(quiet=quiet)
-
-    Main, init_log = init_julia(julia_project, quiet=quiet, return_aux=True)
-    io_arg = _get_io_arg(quiet)
-
-    if precompile is None:
-        precompile = init_log["compiled_modules"]
-
-    if not precompile:
-        Main.eval('ENV["JULIA_PKG_PRECOMPILE_AUTO"] = 0')
-
-    if is_shared:
-        # Install EquivalentCircuits.jl:
-        _add_ec_to_julia_project(Main, io_arg)
-
-    Main.eval("using Pkg")
-    Main.eval(f"Pkg.instantiate({io_arg})")
-
-    if precompile:
-        Main.eval(f"Pkg.precompile({io_arg})")
-
-    if not quiet:
-        log.warning(
-            "It is recommended to restart Python after installing AutoEIS "
-            "dependencies, so that the Julia environment is properly initialized."
-        )
-
-
-def init_julia(julia_project=None, quiet=False, julia_kwargs=None, return_aux=False):
-    """Initialize julia binary, turning off compiled modules if needed."""
-    global julia_initialized
-    global julia_kwargs_at_initialization
-    global julia_activated_env
-
-    if julia_kwargs is None:
-        julia_kwargs = {"optimize": 3}
-
-    from julia.core import JuliaInfo, UnsupportedPythonError
-
-    _julia_version_assertion()
-    processed_julia_project, is_shared = _process_julia_project(julia_project)
-    _set_julia_project_env(processed_julia_project, is_shared)
-
-    try:
-        info = JuliaInfo.load(julia="julia")
-    except FileNotFoundError:
-        _raise_julia_not_found()
-
-    if not info.is_pycall_built():
-        _raise_import_error()
-
-    from julia.core import Julia
-
-    try:
-        Julia(**julia_kwargs)
-    except UnsupportedPythonError:
-        # HACK: When switching virtualenvs, we also get UnsupportedPythonError.
-        # Check if it resolves by recompiling PyCall, if not turn off compiled_modules
-        _recompile_pycall()
-        try:
-            Julia(**julia_kwargs)
-        except UnsupportedPythonError:
-            # Static python binary, so we turn off pre-compiled modules.
-            julia_kwargs = {**julia_kwargs, "compiled_modules": False}
-            Julia(**julia_kwargs)
-            log.warning(
-                "Your system's Python library is static (e.g., conda), "
-                "so precompilation will be turned off. For a dynamic library, "
-                "try using `pyenv` and installing with `--enable-shared`: "
-                "https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md"
-            )
-
-    using_compiled_modules = ("compiled_modules" not in julia_kwargs) or julia_kwargs[
-        "compiled_modules"
-    ]
-
-    from julia import Main as _Main
-
-    Main = _Main
-
-    if julia_activated_env is None:
-        julia_activated_env = processed_julia_project
-
-    if julia_initialized and julia_kwargs_at_initialization is not None:
-        # Check if the kwargs are the same as the previous initialization
-        init_set = set(julia_kwargs_at_initialization.items())
-        new_set = set(julia_kwargs.items())
-        set_diff = new_set - init_set
-        # Remove the `compiled_modules` key, since it is not a user-specified kwarg:
-        set_diff = {k: v for k, v in set_diff if k != "compiled_modules"}
-        if len(set_diff) > 0:
-            log.warning(
-                f"Julia has already started. The new Julia options {set_diff} "
-                "will be ignored."
-            )
-
-    if julia_initialized and julia_activated_env != processed_julia_project:
-        Main.eval("using Pkg")
-
-        io_arg = _get_io_arg(quiet)
-        # Can't pass IO to Julia call as it evaluates to PyObject, so just directly
-        # use Main.eval:
-        Main.eval(
-            f'Pkg.activate("{_escape_filename(processed_julia_project)}",'
-            f"shared = Bool({int(is_shared)}), "
-            f"{io_arg})"
-        )
-
-        julia_activated_env = processed_julia_project
-
-    if not julia_initialized:
-        julia_kwargs_at_initialization = julia_kwargs
-
-    julia_initialized = True
-    if return_aux:
-        return Main, {"compiled_modules": using_compiled_modules}
-    return Main
-
-
-def import_backend(Main):
-    """Load EquivalentCircuits.jl, verify version and return a reference."""
-    ec = import_package("EquivalentCircuits", Main=Main)
-    # FIXME: Currently don't know how to assert branch name if installed from GitHub
-    if __equivalent_circuits_jl_version__.startswith("v"):
-        _backend_version_assertion(Main)
-    return ec
-
-
-def import_package(pkg_name, Main):
-    """Load a Julia package and return a reference to it."""
-    # HACK: On Windows, for some reason, the first two imports fail!
-    for _ in range(MAX_RETRIES):
-        try:
-            Main.eval(f"using {pkg_name}")
-            break
-        except Exception:
-            pass
+    kwargs = {"name": "EquivalentCircuits", "uuid": "da5bd070-f609-4e16-a30d-de86b3faa756"}
+    if ec_path is not None:
+        kwargs["path"] = ec_path
+        kwargs["dev"] = True
     else:
-        # Import failed, raise proper error
-        try:
-            Main.eval(f"using {pkg_name}")
-        except (JuliaError, RuntimeError) as e:
-            _raise_import_error(root=e)
-    return importlib.import_module(f"julia.{pkg_name}")
-
-
-def add_to_path(path):
-    if not os.path.exists(path):
-        raise ValueError(f"The provided path '{path}' does not exist.")
-
-    if path not in os.environ["PATH"].split(os.pathsep):
-        os.environ["PATH"] += os.pathsep + path
-
-
-def _recompile_pycall():
-    import julia
-
-    try:
-        os.environ["PYCALL_JL_RUNTIME_PYTHON"] = sys.executable
-        julia.install(quiet=True)
-    except julia.tools.PyCallInstallError:
-        pass
-
-
-def _raise_import_error(root: Exception = None):
-    """Raise ImportError if Julia dependencies are not installed."""
-    raise ImportError(
-        "Required dependencies are not installed or built. Run the "
-        "following command: import autoeis; autoeis.julia_helpers.install()."
-    ) from root
-
-
-def _raise_julia_not_found(root: Exception = None):
-    """Raise FileNotFoundError if Julia is not installed."""
-    raise FileNotFoundError(
-        "Julia is not installed in your PATH. Please install Julia "
-        "and add it to your PATH."
-    ) from root
-
-
-def _load_juliainfo():
-    """Execute julia.core.JuliaInfo.load(), and store as juliainfo."""
-    global juliainfo
-
-    if juliainfo is None:
-        from julia.core import JuliaInfo
-
-        try:
-            juliainfo = JuliaInfo.load(julia="julia")
-        except FileNotFoundError:
-            _raise_julia_not_found()
-
-    return juliainfo
-
-
-def _get_julia_env_dir():
-    """Find the Julia environments' directory."""
-    try:
-        julia_env_dir_str = subprocess.run(
-            ["julia", "-e using Pkg; print(Pkg.envdir())"],
-            capture_output=True,
-            env=os.environ,
-        ).stdout.decode()
-    except FileNotFoundError:
-        env_path = os.environ["PATH"]
-        _raise_julia_not_found()
-    return Path(julia_env_dir_str)
-
-
-def _set_julia_project_env(julia_project, is_shared):
-    """Set JULIA_PROJECT environment variable."""
-    if is_shared:
-        if _is_julia_version_greater_eq(version=(1, 7, 0)):
-            os.environ["JULIA_PROJECT"] = "@" + str(julia_project)
+        if __equivalent_circuits_jl_version__.startswith("v"):
+            kwargs["version"] = __equivalent_circuits_jl_version__
         else:
-            julia_env_dir = _get_julia_env_dir()
-            os.environ["JULIA_PROJECT"] = str(julia_env_dir / julia_project)
-    else:
-        os.environ["JULIA_PROJECT"] = str(julia_project)
-
-
-def _get_io_arg(quiet):
-    """Return Julia-compatible IO arg that suppresses output if quiet=True."""
-    io = "devnull" if quiet else "stderr"
-    io_arg = f"io={io}" if _is_julia_version_greater_eq(version=(1, 6, 0)) else ""
-    return io_arg
-
-
-def _process_julia_project(julia_project):
-    if julia_project is None:
-        is_shared = True
-        processed_julia_project = f"autoeis-{__version__}"
-    elif julia_project[0] == "@":
-        is_shared = True
-        processed_julia_project = julia_project[1:]
-    else:
-        is_shared = False
-        processed_julia_project = Path(julia_project)
-    return processed_julia_project, is_shared
+            kwargs["rev"] = __equivalent_circuits_jl_version__
+            kwargs["url"] = "https://github.com/ma-sadeghi/EquivalentCircuits.jl"
+    pkg_spec = juliapkg.PkgSpec(**kwargs)
+    juliapkg.add(pkg_spec)
+    juliapkg.resolve()
 
 
-def _is_julia_version_greater_eq(juliainfo=None, version=(1, 6, 0)):
-    """Check if Julia version is greater than specified version."""
-    if juliainfo is None:
-        juliainfo = _load_juliainfo()
-    current_version = (
-        juliainfo.version_major,
-        juliainfo.version_minor,
-        juliainfo.version_patch,
-    )
-    return current_version >= version
-
-
-def _add_ec_to_julia_project(Main, io_arg):
-    """Install EquivalentCircuits.jl and dependencies to the Julia project."""
-    Main.eval("using Pkg")
-    Main.eval(f"Pkg.Registry.update({io_arg})")
-    kwargs = {"name": "EquivalentCircuits"}
-    if __equivalent_circuits_jl_version__.startswith("v"):
-        kwargs["version"] = __equivalent_circuits_jl_version__
-    else:
-        kwargs["rev"] = __equivalent_circuits_jl_version__
-    Main.ec_spec = Main.PackageSpec(**kwargs)
-    Main.eval(f"Pkg.add([ec_spec], {io_arg})")
-
-
-def _escape_filename(filename):
-    """Turn a path into a string with correctly escaped backslashes."""
-    str_repr = str(filename)
-    str_repr = str_repr.replace("\\", "\\\\")
-    return str_repr
-
-
-def _julia_version_assertion():
-    """Check if Julia version is greater than 1.9"""
-    if not _is_julia_version_greater_eq(version=(1, 9, 0)):
-        raise NotImplementedError(
-            "AutoEIS requires Julia 1.9.0 or greater. "
-            "Please update your Julia installation."
-        )
+def init_julia():
+    """Initializes Julia and returns the Main module."""
+    is_julia_installed(error=True, install=False)
+    from juliacall import Main
 
+    return Main
 
-def _backend_version_assertion(Main):
-    """Check if EquivalentCircuits.jl version is correct."""
-    try:
-        backend_version = Main.eval("string(pkgversion(EquivalentCircuits))")
-        expected_backend_version = __equivalent_circuits_jl_version__
-        if backend_version != expected_backend_version:  # pragma: no cover
-            log.warning(
-                f"AutoEIS backend (EquivalentCircuits.jl) version {backend_version} "
-                f"does not match expected version {expected_backend_version}. "
-                "Things may break. Please update your AutoEIS installation with "
-                "`import autoeis; autoeis.julia_helpers.install()`."
-            )
-    except JuliaError:  # pragma: no cover
-        log.warning(
-            "You seem to have an outdated version of EquivalentCircuits.jl. "
-            "Things may break. Please update your AutoEIS installation with "
-            "`import autoeis; autoeis.julia_helpers.install()`."
-        )
 
+def import_package(package_name, Main, error=False):
+    """Imports a package in Julia and returns the module."""
+    from juliacall import JuliaError
 
-def _update_julia_project(Main, is_shared, io_arg):
     try:
-        if is_shared:
-            _add_ec_to_julia_project(Main, io_arg)
-        Main.eval("using Pkg")
-        Main.eval(f"Pkg.resolve({io_arg})")
-    except (JuliaError, RuntimeError) as e:
-        _raise_import_error(root=e)
+        Main.seval(f"using {package_name}")
+        return eval(f"Main.{package_name}")
+    except JuliaError as e:
+        if error:
+            raise e
+    return None
+
+
+def import_backend(Main=None):
+    """Imports EquivalentCircuits package from Julia."""
+    Main = init_julia() if Main is None else Main
+    is_backend_installed(error=True, install=False)
+    return import_package("EquivalentCircuits", Main)
+
+
+def is_julia_installed(error=False, install=False):
+    """Asserts that Julia is installed."""
+    julia_installed_sys = ju_find_julia_noinstall()
+    julia_installed_exe = juliapkg.deps.can_skip_resolve()
+    if julia_installed_sys or julia_installed_exe:
+        return True
+    if install:
+        log.warning("Julia not found, installing Julia...")
+        with suppress_output():
+            install_julia()
+        return True
+    msg = "Julia not found. Visit https://github.com/JuliaLang/juliaup and install Julia."
+    if error:
+        raise ImportError(msg)
+    return False
+
+
+def is_backend_installed(Main=None, error=False, install=False):
+    """Asserts that EquivalentCircuits.jl is installed."""
+    Main = init_julia() if Main is None else Main
+    if import_package("EquivalentCircuits", Main, error=False) is not None:
+        return True
+    if install:
+        log.warning("EquivalentCircuits.jl not found, installing...")
+        with suppress_output():
+            install_backend()
+        return True
+    msg = "EquivalentCircuits.jl not found, run 'python -m autoeis install'"
+    if error:
+        raise ImportError(msg)
+    return False
+
+
+def ensure_julia_deps_ready():
+    """Ensures Julia and EquivalentCircuits.jl are installed."""
+    is_julia_installed(error=True, install=True)
+    is_backend_installed(error=True, install=True)
diff --git a/autoeis/julia_helpers_pyjulia.py b/autoeis/julia_helpers_pyjulia.py
new file mode 100644
index 00000000..cb426791
--- /dev/null
+++ b/autoeis/julia_helpers_pyjulia.py
@@ -0,0 +1,368 @@
+# This code is based on the file julia_helpers.py from the PySR project.
+# It has been adapted to suit the requirements of AutoEIS.
+# The original file is under the Apache 2.0 license.
+# Acknowledge the original authors and license when utilizing this code.
+# Original repository: https://github.com/MilesCranmer/PySR
+# Commit reference: 976f8d8 dated 2023-09-16.
+
+import importlib
+import os
+import subprocess
+import sys
+from pathlib import Path
+
+from julia.api import JuliaError
+
+import autoeis.utils as utils
+
+from .version import __equivalent_circuits_jl_version__, __version__
+
+MAX_RETRIES = 2
+juliainfo = None
+julia_initialized = False
+julia_kwargs_at_initialization = None
+julia_activated_env = None
+
+log = utils.get_logger(__name__)
+# TODO: For virtualenvs see https://github.com/JuliaPy/PyCall.jl?tab=readme-ov-file#python-virtual-environments
+
+
+def install(
+    julia_project=None, quiet=False, precompile=None, offline=False
+):  # pragma: no cover
+    """Install all required dependencies for EquivalentCircuits.jl."""
+    import julia
+
+    _julia_version_assertion()
+    # Set JULIA_PROJECT so that we install in the autoeis environment
+    processed_julia_project, is_shared = _process_julia_project(julia_project)
+    _set_julia_project_env(processed_julia_project, is_shared)
+
+    if precompile is False:
+        os.environ["JULIA_PKG_PRECOMPILE_AUTO"] = "0"
+
+    if offline:
+        os.environ["JULIA_PKG_OFFLINE"] = "true"
+
+    try:
+        julia.install(quiet=quiet)
+    except julia.tools.PyCallInstallError:
+        # Attempt to reset PyCall.jl's build:
+        subprocess.run(
+            [
+                "julia",
+                "-e",
+                f'ENV["PYTHON"] = "{sys.executable}"; import Pkg; Pkg.build("PyCall")',
+            ],
+        )
+        # Try installing again:
+        julia.install(quiet=quiet)
+
+    Main, init_log = init_julia(julia_project, quiet=quiet, return_aux=True)
+    io_arg = _get_io_arg(quiet)
+
+    if precompile is None:
+        precompile = init_log["compiled_modules"]
+
+    if not precompile:
+        Main.eval('ENV["JULIA_PKG_PRECOMPILE_AUTO"] = 0')
+
+    if is_shared:
+        # Install EquivalentCircuits.jl:
+        _add_ec_to_julia_project(Main, io_arg)
+
+    Main.eval("using Pkg")
+    Main.eval(f"Pkg.instantiate({io_arg})")
+
+    if precompile:
+        Main.eval(f"Pkg.precompile({io_arg})")
+
+    if not quiet:
+        log.warning(
+            "It is recommended to restart Python after installing AutoEIS "
+            "dependencies, so that the Julia environment is properly initialized."
+        )
+
+
+def init_julia(julia_project=None, quiet=False, julia_kwargs=None, return_aux=False):
+    """Initialize julia binary, turning off compiled modules if needed."""
+    global julia_initialized
+    global julia_kwargs_at_initialization
+    global julia_activated_env
+
+    if julia_kwargs is None:
+        julia_kwargs = {"optimize": 3}
+
+    from julia.core import JuliaInfo, UnsupportedPythonError
+
+    _julia_version_assertion()
+    processed_julia_project, is_shared = _process_julia_project(julia_project)
+    _set_julia_project_env(processed_julia_project, is_shared)
+
+    try:
+        info = JuliaInfo.load(julia="julia")
+    except FileNotFoundError:
+        _raise_julia_not_found()
+
+    if not info.is_pycall_built():
+        _raise_import_error()
+
+    from julia.core import Julia
+
+    try:
+        Julia(**julia_kwargs)
+    except UnsupportedPythonError:
+        # HACK: When switching virtualenvs, we also get UnsupportedPythonError.
+        # Check if it resolves by recompiling PyCall, if not turn off compiled_modules
+        _recompile_pycall()
+        try:
+            Julia(**julia_kwargs)
+        except UnsupportedPythonError:
+            # Static python binary, so we turn off pre-compiled modules.
+            julia_kwargs = {**julia_kwargs, "compiled_modules": False}
+            Julia(**julia_kwargs)
+            log.warning(
+                "Your system's Python library is static (e.g., conda), "
+                "so precompilation will be turned off. For a dynamic library, "
+                "try using `pyenv` and installing with `--enable-shared`: "
+                "https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.md"
+            )
+
+    using_compiled_modules = ("compiled_modules" not in julia_kwargs) or julia_kwargs[
+        "compiled_modules"
+    ]
+
+    from julia import Main as _Main
+
+    Main = _Main
+
+    if julia_activated_env is None:
+        julia_activated_env = processed_julia_project
+
+    if julia_initialized and julia_kwargs_at_initialization is not None:
+        # Check if the kwargs are the same as the previous initialization
+        init_set = set(julia_kwargs_at_initialization.items())
+        new_set = set(julia_kwargs.items())
+        set_diff = new_set - init_set
+        # Remove the `compiled_modules` key, since it is not a user-specified kwarg:
+        set_diff = {k: v for k, v in set_diff if k != "compiled_modules"}
+        if len(set_diff) > 0:
+            log.warning(
+                f"Julia has already started. The new Julia options {set_diff} "
+                "will be ignored."
+            )
+
+    if julia_initialized and julia_activated_env != processed_julia_project:
+        Main.eval("using Pkg")
+
+        io_arg = _get_io_arg(quiet)
+        # Can't pass IO to Julia call as it evaluates to PyObject, so just directly
+        # use Main.eval:
+        Main.eval(
+            f'Pkg.activate("{_escape_filename(processed_julia_project)}",'
+            f"shared = Bool({int(is_shared)}), "
+            f"{io_arg})"
+        )
+
+        julia_activated_env = processed_julia_project
+
+    if not julia_initialized:
+        julia_kwargs_at_initialization = julia_kwargs
+
+    julia_initialized = True
+    if return_aux:
+        return Main, {"compiled_modules": using_compiled_modules}
+    return Main
+
+
+def import_backend(Main):
+    """Load EquivalentCircuits.jl, verify version and return a reference."""
+    ec = import_package("EquivalentCircuits", Main=Main)
+    # FIXME: Currently don't know how to assert branch name if installed from GitHub
+    if __equivalent_circuits_jl_version__.startswith("v"):
+        _backend_version_assertion(Main)
+    return ec
+
+
+def import_package(pkg_name, Main):
+    """Load a Julia package and return a reference to it."""
+    # HACK: On Windows, for some reason, the first two imports fail!
+    for _ in range(MAX_RETRIES):
+        try:
+            Main.eval(f"using {pkg_name}")
+            break
+        except Exception:
+            pass
+    else:
+        # Import failed, raise proper error
+        try:
+            Main.eval(f"using {pkg_name}")
+        except (JuliaError, RuntimeError) as e:
+            _raise_import_error(root=e)
+    return importlib.import_module(f"julia.{pkg_name}")
+
+
+def add_to_path(path):
+    if not os.path.exists(path):
+        raise ValueError(f"The provided path '{path}' does not exist.")
+
+    if path not in os.environ["PATH"].split(os.pathsep):
+        os.environ["PATH"] += os.pathsep + path
+
+
+def _recompile_pycall():
+    import julia
+
+    try:
+        os.environ["PYCALL_JL_RUNTIME_PYTHON"] = sys.executable
+        julia.install(quiet=True)
+    except julia.tools.PyCallInstallError:
+        pass
+
+
+def _raise_import_error(root: Exception = None):
+    """Raise ImportError if Julia dependencies are not installed."""
+    raise ImportError(
+        "Required dependencies are not installed or built. Run the "
+        "following command: import autoeis; autoeis.julia_helpers.install()."
+    ) from root
+
+
+def _raise_julia_not_found(root: Exception = None):
+    """Raise FileNotFoundError if Julia is not installed."""
+    raise FileNotFoundError(
+        "Julia is not installed in your PATH. Please install Julia "
+        "and add it to your PATH."
+    ) from root
+
+
+def _load_juliainfo():
+    """Execute julia.core.JuliaInfo.load(), and store as juliainfo."""
+    global juliainfo
+
+    if juliainfo is None:
+        from julia.core import JuliaInfo
+
+        try:
+            juliainfo = JuliaInfo.load(julia="julia")
+        except FileNotFoundError:
+            _raise_julia_not_found()
+
+    return juliainfo
+
+
+def _get_julia_env_dir():
+    """Find the Julia environments' directory."""
+    try:
+        julia_env_dir_str = subprocess.run(
+            ["julia", "-e using Pkg; print(Pkg.envdir())"],
+            capture_output=True,
+            env=os.environ,
+        ).stdout.decode()
+    except FileNotFoundError:
+        env_path = os.environ["PATH"]
+        _raise_julia_not_found()
+    return Path(julia_env_dir_str)
+
+
+def _set_julia_project_env(julia_project, is_shared):
+    """Set JULIA_PROJECT environment variable."""
+    if is_shared:
+        if _is_julia_version_greater_eq(version=(1, 7, 0)):
+            os.environ["JULIA_PROJECT"] = "@" + str(julia_project)
+        else:
+            julia_env_dir = _get_julia_env_dir()
+            os.environ["JULIA_PROJECT"] = str(julia_env_dir / julia_project)
+    else:
+        os.environ["JULIA_PROJECT"] = str(julia_project)
+
+
+def _get_io_arg(quiet):
+    """Return Julia-compatible IO arg that suppresses output if quiet=True."""
+    io = "devnull" if quiet else "stderr"
+    io_arg = f"io={io}" if _is_julia_version_greater_eq(version=(1, 6, 0)) else ""
+    return io_arg
+
+
+def _process_julia_project(julia_project):
+    if julia_project is None:
+        is_shared = True
+        processed_julia_project = f"autoeis-{__version__}"
+    elif julia_project[0] == "@":
+        is_shared = True
+        processed_julia_project = julia_project[1:]
+    else:
+        is_shared = False
+        processed_julia_project = Path(julia_project)
+    return processed_julia_project, is_shared
+
+
+def _is_julia_version_greater_eq(juliainfo=None, version=(1, 6, 0)):
+    """Check if Julia version is greater than specified version."""
+    if juliainfo is None:
+        juliainfo = _load_juliainfo()
+    current_version = (
+        juliainfo.version_major,
+        juliainfo.version_minor,
+        juliainfo.version_patch,
+    )
+    return current_version >= version
+
+
+def _add_ec_to_julia_project(Main, io_arg):
+    """Install EquivalentCircuits.jl and dependencies to the Julia project."""
+    Main.eval("using Pkg")
+    Main.eval(f"Pkg.Registry.update({io_arg})")
+    kwargs = {"name": "EquivalentCircuits"}
+    if __equivalent_circuits_jl_version__.startswith("v"):
+        kwargs["version"] = __equivalent_circuits_jl_version__
+    else:
+        kwargs["rev"] = __equivalent_circuits_jl_version__
+    Main.ec_spec = Main.PackageSpec(**kwargs)
+    Main.eval(f"Pkg.add([ec_spec], {io_arg})")
+
+
+def _escape_filename(filename):
+    """Turn a path into a string with correctly escaped backslashes."""
+    str_repr = str(filename)
+    str_repr = str_repr.replace("\\", "\\\\")
+    return str_repr
+
+
+def _julia_version_assertion():
+    """Check if Julia version is greater than 1.9"""
+    if not _is_julia_version_greater_eq(version=(1, 9, 0)):
+        raise NotImplementedError(
+            "AutoEIS requires Julia 1.9.0 or greater. "
+            "Please update your Julia installation."
+        )
+
+
+def _backend_version_assertion(Main):
+    """Check if EquivalentCircuits.jl version is correct."""
+    try:
+        backend_version = Main.eval("string(pkgversion(EquivalentCircuits))")
+        expected_backend_version = __equivalent_circuits_jl_version__
+        if backend_version != expected_backend_version:  # pragma: no cover
+            log.warning(
+                f"AutoEIS backend (EquivalentCircuits.jl) version {backend_version} "
+                f"does not match expected version {expected_backend_version}. "
+                "Things may break. Please update your AutoEIS installation with "
+                "`import autoeis; autoeis.julia_helpers.install()`."
+            )
+    except JuliaError:  # pragma: no cover
+        log.warning(
+            "You seem to have an outdated version of EquivalentCircuits.jl. "
+            "Things may break. Please update your AutoEIS installation with "
+            "`import autoeis; autoeis.julia_helpers.install()`."
+        )
+
+
+def _update_julia_project(Main, is_shared, io_arg):
+    try:
+        if is_shared:
+            _add_ec_to_julia_project(Main, io_arg)
+        Main.eval("using Pkg")
+        Main.eval(f"Pkg.resolve({io_arg})")
+    except (JuliaError, RuntimeError) as e:
+        _raise_import_error(root=e)
diff --git a/autoeis/utils.py b/autoeis/utils.py
index adf4dc76..315da415 100644
--- a/autoeis/utils.py
+++ b/autoeis/utils.py
@@ -22,6 +22,7 @@
 import signal
 import sys
 from collections.abc import Iterable
+from contextlib import contextmanager
 from functools import partial, wraps
 from typing import Union
 
@@ -35,10 +36,10 @@
 from rich.logging import RichHandler
 from scipy import stats
 
+# from tensorflow_probability import distributions as tfdist  # NOQA: F401
 import __main__
 
-# from tensorflow_probability import distributions as tfdist  # NOQA: F401
-from autoeis import parser
+from . import parser
 
 # >>> Logging utils
 
@@ -104,8 +105,9 @@ def __exit__(self, exc_type, exc_val, exc_tb):
         sys.stderr = self._original_stderr
 
 
-def suppress_output(func):
+def suppress_output_legacy(func):
     """Suppresses the output of a function."""
+    # NOTE: This approach only works when stdout/stderr are managed by Python
 
     @wraps(func)
     def wrapped(*args, **kwargs):
@@ -115,6 +117,28 @@ def wrapped(*args, **kwargs):
     return wrapped
 
 
+@contextmanager
+def suppress_output():
+    """Suppresses the output of a block of code using file descriptors."""
+    # NOTE: This approach is more system-level than suppress_output
+    # Save the current file descriptors
+    original_stderr_fd = os.dup(2)
+    original_stdout_fd = os.dup(1)
+
+    try:
+        # Use os.devnull to redirect the standard output and standard error
+        with open(os.devnull, "wb") as devnull:
+            os.dup2(devnull.fileno(), 1)
+            os.dup2(devnull.fileno(), 2)
+            yield
+    finally:
+        # Restore the original file descriptors
+        os.dup2(original_stdout_fd, 1)
+        os.dup2(original_stderr_fd, 2)
+        os.close(original_stdout_fd)
+        os.close(original_stderr_fd)
+
+
 class TimeoutException(Exception):
     pass
 
diff --git a/autoeis/version.py b/autoeis/version.py
index 418f09d6..c0b23606 100644
--- a/autoeis/version.py
+++ b/autoeis/version.py
@@ -1,3 +1,3 @@
 __version__ = "0.0.23"
 # Either use a branch name or a tag name for EquivalentCircuits.jl
-__equivalent_circuits_jl_version__ = "master"  # "0.3.1"
+__equivalent_circuits_jl_version__ = "bug/fix-ndarray-serialization"  # "0.3.1"
diff --git a/examples/autoeis_demo.ipynb b/examples/autoeis_demo.ipynb
index 0c3911dc..ab22f12d 100644
--- a/examples/autoeis_demo.ipynb
+++ b/examples/autoeis_demo.ipynb
@@ -18,53 +18,16 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "AutoEIS relies on `EquivalentCircuits.jl` package to perform the EIS analysis. The package is not written in Python, so we need to install it first. AutoEIS ships with `julia_helpers` module that helps to install and manage Julia dependencies with minimal user interaction. The only thing you need to do is to run `autoeis.julia_helpers.install()`, and everything will be taken care of. You only need to do this once."
+    "AutoEIS relies on `EquivalentCircuits.jl` package to perform the EIS analysis. The package is not written in Python, so we need to install it first. AutoEIS ships with `julia_helpers` module that helps to install and manage Julia dependencies with minimal user interaction. For convenience, installing Julia and the required packages is done automatically when you import `autoeis` for the first time. If you have Julia installed already (discoverable in system PATH), it'll get detected and used, otherwise, it'll be installed automatically."
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 6,
+   "execution_count": 2,
    "metadata": {
     "scrolled": true
    },
-   "outputs": [
-    {
-     "data": {
-      "text/html": [
-       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #7fbfbf; text-decoration-color: #7fbfbf\">[09:24:22] </span><span style=\"color: #800000; text-decoration-color: #800000\">WARNING </span> If you use the Python distribution by Conda, the initialization of the package    <a href=\"file:///home/amin/Code/AutoEIS/autoeis/__init__.py\" target=\"_blank\"><span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">__init__.py</span></a><span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">:</span><a href=\"file:///home/amin/Code/AutoEIS/autoeis/__init__.py#4\" target=\"_blank\"><span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">4</span></a>\n",
-       "<span style=\"color: #7fbfbf; text-decoration-color: #7fbfbf\">           </span>         may take <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span>-<span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2</span> minutes. Be patient :<span style=\"font-weight: bold\">)</span>                                               <span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">             </span>\n",
-       "</pre>\n"
-      ],
-      "text/plain": [
-       "\u001b[2;36m[09:24:22]\u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m If you use the Python distribution by Conda, the initialization of the package    \u001b]8;id=513428;file:///home/amin/Code/AutoEIS/autoeis/__init__.py\u001b\\\u001b[2m__init__.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=861758;file:///home/amin/Code/AutoEIS/autoeis/__init__.py#4\u001b\\\u001b[2m4\u001b[0m\u001b]8;;\u001b\\\n",
-       "\u001b[2;36m           \u001b[0m         may take \u001b[1;36m1\u001b[0m-\u001b[1;36m2\u001b[0m minutes. Be patient :\u001b[1m)\u001b[0m                                               \u001b[2m             \u001b[0m\n"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "text/html": [
-       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #7fbfbf; text-decoration-color: #7fbfbf\">[09:24:41 AM] </span><span style=\"color: #800000; text-decoration-color: #800000\">WARNING </span> Your system's Python library is static <span style=\"font-weight: bold\">(</span>e.g., conda<span style=\"font-weight: bold\">)</span>, so precompilation <a href=\"file:///home/amin/Code/AutoEIS/autoeis/julia_helpers.py\" target=\"_blank\"><span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">julia_helpers.py</span></a><span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">:</span><a href=\"file:///home/amin/Code/AutoEIS/autoeis/julia_helpers.py#124\" target=\"_blank\"><span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">124</span></a>\n",
-       "<span style=\"color: #7fbfbf; text-decoration-color: #7fbfbf\">              </span>         will be turned off. For a dynamic library, try using `pyenv` and        <span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">                    </span>\n",
-       "<span style=\"color: #7fbfbf; text-decoration-color: #7fbfbf\">              </span>         installing with `--enable-shared`:                                      <span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">                    </span>\n",
-       "<span style=\"color: #7fbfbf; text-decoration-color: #7fbfbf\">              </span>         <span style=\"color: #0000ff; text-decoration-color: #0000ff; text-decoration: underline\">https://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.</span> <span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">                    </span>\n",
-       "<span style=\"color: #7fbfbf; text-decoration-color: #7fbfbf\">              </span>         <span style=\"color: #0000ff; text-decoration-color: #0000ff; text-decoration: underline\">md</span>                                                                      <span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\">                    </span>\n",
-       "</pre>\n"
-      ],
-      "text/plain": [
-       "\u001b[2;36m[09:24:41 AM]\u001b[0m\u001b[2;36m \u001b[0m\u001b[31mWARNING \u001b[0m Your system's Python library is static \u001b[1m(\u001b[0me.g., conda\u001b[1m)\u001b[0m, so precompilation \u001b]8;id=181980;file:///home/amin/Code/AutoEIS/autoeis/julia_helpers.py\u001b\\\u001b[2mjulia_helpers.py\u001b[0m\u001b]8;;\u001b\\\u001b[2m:\u001b[0m\u001b]8;id=100978;file:///home/amin/Code/AutoEIS/autoeis/julia_helpers.py#124\u001b\\\u001b[2m124\u001b[0m\u001b]8;;\u001b\\\n",
-       "\u001b[2;36m              \u001b[0m         will be turned off. For a dynamic library, try using `pyenv` and        \u001b[2m                    \u001b[0m\n",
-       "\u001b[2;36m              \u001b[0m         installing with `--enable-shared`:                                      \u001b[2m                    \u001b[0m\n",
-       "\u001b[2;36m              \u001b[0m         \u001b[4;94mhttps://github.com/pyenv/pyenv/blob/master/plugins/python-build/README.\u001b[0m \u001b[2m                    \u001b[0m\n",
-       "\u001b[2;36m              \u001b[0m         \u001b[4;94mmd\u001b[0m                                                                      \u001b[2m                    \u001b[0m\n"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    }
-   ],
+   "outputs": [],
    "source": [
     "import os\n",
     "\n",
@@ -102,7 +65,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 9,
+   "execution_count": 3,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -123,7 +86,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 10,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [
     {
@@ -168,7 +131,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 11,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [
     {
@@ -204,23 +167,33 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "application/vnd.jupyter.widget-view+json": {
-       "model_id": "8b4796c9017647148ba57465d52c97ba",
+       "model_id": "af8bd6b94fae40e78dd6c5e3cc712d99",
        "version_major": 2,
        "version_minor": 0
       },
       "text/plain": [
-       "Circuit Evolution:   0%|          | 0/12 [00:00<?, ?it/s]"
+       "Circuit Evolution:   0%|          | 0/24 [00:00<?, ?it/s]"
       ]
      },
      "metadata": {},
      "output_type": "display_data"
     },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "[ Info: Starting circuit evolution on 6 workers\n",
+      "[ Info: Starting circuit evolution on 6 workers\n",
+      "[ Info: Starting circuit evolution on 6 workers\n",
+      "[ Info: Starting circuit evolution on 6 workers\n"
+     ]
+    },
     {
      "data": {
       "text/html": [
@@ -249,73 +222,157 @@
        "  <tbody>\n",
        "    <tr>\n",
        "      <th>0</th>\n",
-       "      <td>[R1-P2,R3]</td>\n",
-       "      <td>{'R1': 139.1513124119036, 'P2w': 1.98653501035...</td>\n",
+       "      <td>[C1-R2,R3-P4-[P5,R6]]</td>\n",
+       "      <td>{'C1': 1.2518230659909423e-06, 'R2': 158.85747...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>1</th>\n",
-       "      <td>[P1,P2]-R3</td>\n",
-       "      <td>{'P1w': 4.0702380949446655e-07, 'P1n': 0.16606...</td>\n",
+       "      <td>[R1-C2,P3-R4]</td>\n",
+       "      <td>{'R1': 146.56715696639486, 'C2': 1.47824291323...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>2</th>\n",
-       "      <td>[R1,L2]-[R3,[R4,P5]]</td>\n",
-       "      <td>{'R1': 852044549.9300162, 'L2': 3.366919083295...</td>\n",
+       "      <td>C1-[R2,P3]-R4</td>\n",
+       "      <td>{'C1': 6.429085835426775e-05, 'R2': 4086839.40...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>3</th>\n",
-       "      <td>C1-[R2,P3]-P4-[P5,P6]</td>\n",
-       "      <td>{'C1': 0.00039509854539193, 'R2': 2315957.4399...</td>\n",
+       "      <td>[R1,P2-R3-L4-[R5,L6]]</td>\n",
+       "      <td>{'R1': 4629990.101374098, 'P2w': 1.98653501071...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>4</th>\n",
-       "      <td>[R1-P2,R3]</td>\n",
-       "      <td>{'R1': 139.1513124988748, 'P2w': 1.98653501145...</td>\n",
+       "      <td>[P1-R2,R3]</td>\n",
+       "      <td>{'P1w': 2.0968493965977216e-08, 'P1n': 0.99999...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>5</th>\n",
-       "      <td>[R1-P2,R3]</td>\n",
-       "      <td>{'R1': 139.15131242529387, 'P2w': 1.9865350115...</td>\n",
+       "      <td>[P1,R2]-R3</td>\n",
+       "      <td>{'P1w': 1.986654419718411e-06, 'P1n': 0.937307...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>6</th>\n",
-       "      <td>[P1,[P2,R3]-[L4-R5,P6]]</td>\n",
-       "      <td>{'P1w': 4.1376293380257665e-07, 'P1n': 0.17075...</td>\n",
+       "      <td>[R1-P2,R3]</td>\n",
+       "      <td>{'R1': 139.15131244656453, 'P2w': 1.9865350108...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>7</th>\n",
-       "      <td>P1-R2-L3-[P4,R5]</td>\n",
-       "      <td>{'P1w': 3.060047761792544e-05, 'P1n': 0.865667...</td>\n",
+       "      <td>[P1-R2,R3]</td>\n",
+       "      <td>{'P1w': 2.3702129309162755e-08, 'P1n': 0.99999...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>8</th>\n",
-       "      <td>P1-[R2,P3]-R4</td>\n",
-       "      <td>{'P1w': 3.0600477908068945e-05, 'P1n': 0.86566...</td>\n",
+       "      <td>[P1-R2,R3]</td>\n",
+       "      <td>{'P1w': 1.633555512329923e-08, 'P1n': 0.999999...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>9</th>\n",
+       "      <td>R1-[R2,[R3,P4]]</td>\n",
+       "      <td>{'R1': 139.14713047789797, 'R2': 4654954.87137...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>10</th>\n",
+       "      <td>[P1,R2]-[L3,R4]</td>\n",
+       "      <td>{'P1w': 2.302924485371552e-06, 'P1n': 0.869686...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>11</th>\n",
+       "      <td>[P1,R2]-R3</td>\n",
+       "      <td>{'P1w': 1.9866544198483474e-06, 'P1n': 0.93730...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>12</th>\n",
+       "      <td>[R1,P2-R3]</td>\n",
+       "      <td>{'R1': 4629990.101402926, 'P2w': 1.98653500943...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>13</th>\n",
        "      <td>R1-[P2,R3]</td>\n",
-       "      <td>{'R1': 139.14713053462032, 'P2w': 1.9866544216...</td>\n",
+       "      <td>{'R1': 139.14713045260206, 'P2w': 1.9866544187...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>14</th>\n",
+       "      <td>[R1-P2,P3]</td>\n",
+       "      <td>{'R1': 4847243.661018436, 'P2w': 3.61491996853...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>15</th>\n",
+       "      <td>R1-[P2,R3]</td>\n",
+       "      <td>{'R1': 139.14713057315635, 'P2w': 1.9866544205...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>16</th>\n",
+       "      <td>R1-[P2-L3,R4]</td>\n",
+       "      <td>{'R1': 139.14713044066318, 'P2w': 1.9866544191...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>17</th>\n",
+       "      <td>[P1-R2,R3]</td>\n",
+       "      <td>{'P1w': 1.9865350111025016e-06, 'P1n': 0.93730...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>18</th>\n",
+       "      <td>R1-[P2,R3]</td>\n",
+       "      <td>{'R1': 139.14713059613737, 'P2w': 1.9866544211...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>19</th>\n",
+       "      <td>R1-[R2,P3]</td>\n",
+       "      <td>{'R1': 139.14713072472173, 'R2': 4629850.95198...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>20</th>\n",
+       "      <td>P1-[P2,R3]-[R4,L5]</td>\n",
+       "      <td>{'P1w': 0.0071866376038625815, 'P1n': 2.344409...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>21</th>\n",
+       "      <td>[L1-P2-R3,P4-[R5,L6]]</td>\n",
+       "      <td>{'L1': 4.999999995043704, 'P2w': 3.61491672511...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>22</th>\n",
+       "      <td>P1-[P2-R3,L4-R5-L6]</td>\n",
+       "      <td>{'P1w': 3.0600477978470834e-05, 'P1n': 0.86566...</td>\n",
+       "    </tr>\n",
+       "    <tr>\n",
+       "      <th>23</th>\n",
+       "      <td>[R1,P2]-R3</td>\n",
+       "      <td>{'R1': 4629850.946599953, 'P2w': 1.98665441944...</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>\n",
        "</div>"
       ],
       "text/plain": [
-       "             circuitstring                                         Parameters\n",
-       "0               [R1-P2,R3]  {'R1': 139.1513124119036, 'P2w': 1.98653501035...\n",
-       "1               [P1,P2]-R3  {'P1w': 4.0702380949446655e-07, 'P1n': 0.16606...\n",
-       "2     [R1,L2]-[R3,[R4,P5]]  {'R1': 852044549.9300162, 'L2': 3.366919083295...\n",
-       "3    C1-[R2,P3]-P4-[P5,P6]  {'C1': 0.00039509854539193, 'R2': 2315957.4399...\n",
-       "4               [R1-P2,R3]  {'R1': 139.1513124988748, 'P2w': 1.98653501145...\n",
-       "5               [R1-P2,R3]  {'R1': 139.15131242529387, 'P2w': 1.9865350115...\n",
-       "6  [P1,[P2,R3]-[L4-R5,P6]]  {'P1w': 4.1376293380257665e-07, 'P1n': 0.17075...\n",
-       "7         P1-R2-L3-[P4,R5]  {'P1w': 3.060047761792544e-05, 'P1n': 0.865667...\n",
-       "8            P1-[R2,P3]-R4  {'P1w': 3.0600477908068945e-05, 'P1n': 0.86566...\n",
-       "9               R1-[P2,R3]  {'R1': 139.14713053462032, 'P2w': 1.9866544216..."
+       "            circuitstring                                         Parameters\n",
+       "0   [C1-R2,R3-P4-[P5,R6]]  {'C1': 1.2518230659909423e-06, 'R2': 158.85747...\n",
+       "1           [R1-C2,P3-R4]  {'R1': 146.56715696639486, 'C2': 1.47824291323...\n",
+       "2           C1-[R2,P3]-R4  {'C1': 6.429085835426775e-05, 'R2': 4086839.40...\n",
+       "3   [R1,P2-R3-L4-[R5,L6]]  {'R1': 4629990.101374098, 'P2w': 1.98653501071...\n",
+       "4              [P1-R2,R3]  {'P1w': 2.0968493965977216e-08, 'P1n': 0.99999...\n",
+       "5              [P1,R2]-R3  {'P1w': 1.986654419718411e-06, 'P1n': 0.937307...\n",
+       "6              [R1-P2,R3]  {'R1': 139.15131244656453, 'P2w': 1.9865350108...\n",
+       "7              [P1-R2,R3]  {'P1w': 2.3702129309162755e-08, 'P1n': 0.99999...\n",
+       "8              [P1-R2,R3]  {'P1w': 1.633555512329923e-08, 'P1n': 0.999999...\n",
+       "9         R1-[R2,[R3,P4]]  {'R1': 139.14713047789797, 'R2': 4654954.87137...\n",
+       "10        [P1,R2]-[L3,R4]  {'P1w': 2.302924485371552e-06, 'P1n': 0.869686...\n",
+       "11             [P1,R2]-R3  {'P1w': 1.9866544198483474e-06, 'P1n': 0.93730...\n",
+       "12             [R1,P2-R3]  {'R1': 4629990.101402926, 'P2w': 1.98653500943...\n",
+       "13             R1-[P2,R3]  {'R1': 139.14713045260206, 'P2w': 1.9866544187...\n",
+       "14             [R1-P2,P3]  {'R1': 4847243.661018436, 'P2w': 3.61491996853...\n",
+       "15             R1-[P2,R3]  {'R1': 139.14713057315635, 'P2w': 1.9866544205...\n",
+       "16          R1-[P2-L3,R4]  {'R1': 139.14713044066318, 'P2w': 1.9866544191...\n",
+       "17             [P1-R2,R3]  {'P1w': 1.9865350111025016e-06, 'P1n': 0.93730...\n",
+       "18             R1-[P2,R3]  {'R1': 139.14713059613737, 'P2w': 1.9866544211...\n",
+       "19             R1-[R2,P3]  {'R1': 139.14713072472173, 'R2': 4629850.95198...\n",
+       "20     P1-[P2,R3]-[R4,L5]  {'P1w': 0.0071866376038625815, 'P1n': 2.344409...\n",
+       "21  [L1-P2-R3,P4-[R5,L6]]  {'L1': 4.999999995043704, 'P2w': 3.61491672511...\n",
+       "22    P1-[P2-R3,L4-R5-L6]  {'P1w': 3.0600477978470834e-05, 'P1n': 0.86566...\n",
+       "23             [R1,P2]-R3  {'R1': 4629850.946599953, 'P2w': 1.98665441944..."
       ]
      },
-     "execution_count": 12,
+     "execution_count": 6,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -355,7 +412,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 13,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [
     {
@@ -386,37 +443,31 @@
        "  <tbody>\n",
        "    <tr>\n",
        "      <th>0</th>\n",
-       "      <td>[P1,P2]-R3</td>\n",
-       "      <td>{'P1w': 4.0702380949446655e-07, 'P1n': 0.16606...</td>\n",
+       "      <td>[P1,R2]-R3</td>\n",
+       "      <td>{'P1w': 1.986654419718411e-06, 'P1n': 0.937307...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>1</th>\n",
-       "      <td>P1-R2-L3-[P4,R5]</td>\n",
-       "      <td>{'P1w': 3.060047761792544e-05, 'P1n': 0.865667...</td>\n",
+       "      <td>R1-[R2,[R3,P4]]</td>\n",
+       "      <td>{'R1': 139.14713047789797, 'R2': 4654954.87137...</td>\n",
        "    </tr>\n",
        "    <tr>\n",
        "      <th>2</th>\n",
-       "      <td>P1-[R2,P3]-R4</td>\n",
-       "      <td>{'P1w': 3.0600477908068945e-05, 'P1n': 0.86566...</td>\n",
-       "    </tr>\n",
-       "    <tr>\n",
-       "      <th>3</th>\n",
-       "      <td>R1-[P2,R3]</td>\n",
-       "      <td>{'R1': 139.14713053462032, 'P2w': 1.9866544216...</td>\n",
+       "      <td>R1-[P2-L3,R4]</td>\n",
+       "      <td>{'R1': 139.14713044066318, 'P2w': 1.9866544191...</td>\n",
        "    </tr>\n",
        "  </tbody>\n",
        "</table>\n",
        "</div>"
       ],
       "text/plain": [
-       "      circuitstring                                         Parameters\n",
-       "0        [P1,P2]-R3  {'P1w': 4.0702380949446655e-07, 'P1n': 0.16606...\n",
-       "1  P1-R2-L3-[P4,R5]  {'P1w': 3.060047761792544e-05, 'P1n': 0.865667...\n",
-       "2     P1-[R2,P3]-R4  {'P1w': 3.0600477908068945e-05, 'P1n': 0.86566...\n",
-       "3        R1-[P2,R3]  {'R1': 139.14713053462032, 'P2w': 1.9866544216..."
+       "     circuitstring                                         Parameters\n",
+       "0       [P1,R2]-R3  {'P1w': 1.986654419718411e-06, 'P1n': 0.937307...\n",
+       "1  R1-[R2,[R3,P4]]  {'R1': 139.14713047789797, 'R2': 4654954.87137...\n",
+       "2    R1-[P2-L3,R4]  {'R1': 139.14713044066318, 'P2w': 1.9866544191..."
       ]
      },
-     "execution_count": 13,
+     "execution_count": 7,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -442,7 +493,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 14,
+   "execution_count": 8,
    "metadata": {},
    "outputs": [
     {
@@ -462,12 +513,12 @@
     {
      "data": {
       "application/vnd.jupyter.widget-view+json": {
-       "model_id": "0303d442dd4744ef987cb118b9a274ba",
+       "model_id": "aadc9d9ef12943ff9c35fca9541f9555",
        "version_major": 2,
        "version_minor": 0
       },
       "text/plain": [
-       "  0%|          | 0/4 [00:00<?, ?it/s]"
+       "  0%|          | 0/3 [00:00<?, ?it/s]"
       ]
      },
      "metadata": {},
@@ -492,77 +543,36 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 15,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/html": [
-       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                   [P1,P2]-R3, 0/1000 divergences                    </span>\n",
-       "┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓\n",
-       "┃<span style=\"font-weight: bold\">  Parameter </span>┃<span style=\"font-weight: bold\">     Mean </span>┃<span style=\"font-weight: bold\">      Std </span>┃<span style=\"font-weight: bold\">   Median </span>┃<span style=\"font-weight: bold\">     5.0% </span>┃<span style=\"font-weight: bold\">    95.0% </span>┃\n",
-       "┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━┩\n",
-       "│        P1n │ 2.08e-01 │ 3.27e-04 │ 2.08e-01 │ 2.07e-01 │ 2.08e-01 │\n",
-       "│        P1w │ 4.98e-07 │ 6.91e-10 │ 4.98e-07 │ 4.97e-07 │ 4.99e-07 │\n",
-       "│        P2n │ 1.00e+00 │ 2.46e-05 │ 1.00e+00 │ 1.00e+00 │ 1.00e+00 │\n",
-       "│        P2w │ 1.55e-06 │ 2.94e-09 │ 1.55e-06 │ 1.54e-06 │ 1.55e-06 │\n",
-       "│<span style=\"background-color: #808000\">         R3 </span>│<span style=\"background-color: #808000\"> 4.03e+01 </span>│<span style=\"background-color: #808000\"> 5.44e+01 </span>│<span style=\"background-color: #808000\"> 2.00e+01 </span>│<span style=\"background-color: #808000\"> 1.09e+00 </span>│<span style=\"background-color: #808000\"> 1.47e+02 </span>│\n",
-       "│ sigma_imag │ 5.06e+03 │ 4.63e+01 │ 5.06e+03 │ 4.99e+03 │ 5.14e+03 │\n",
-       "│ sigma_real │ 4.71e+03 │ 4.45e+01 │ 4.71e+03 │ 4.64e+03 │ 4.78e+03 │\n",
-       "└────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘\n",
-       "</pre>\n"
-      ],
-      "text/plain": [
-       "\u001b[3m                   [P1,P2]-R3, 0/1000 divergences                    \u001b[0m\n",
-       "┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓\n",
-       "┃\u001b[1m \u001b[0m\u001b[1m Parameter\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m    Mean\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m     Std\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m  Median\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m    5.0%\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m   95.0%\u001b[0m\u001b[1m \u001b[0m┃\n",
-       "┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━┩\n",
-       "│        P1n │ 2.08e-01 │ 3.27e-04 │ 2.08e-01 │ 2.07e-01 │ 2.08e-01 │\n",
-       "│        P1w │ 4.98e-07 │ 6.91e-10 │ 4.98e-07 │ 4.97e-07 │ 4.99e-07 │\n",
-       "│        P2n │ 1.00e+00 │ 2.46e-05 │ 1.00e+00 │ 1.00e+00 │ 1.00e+00 │\n",
-       "│        P2w │ 1.55e-06 │ 2.94e-09 │ 1.55e-06 │ 1.54e-06 │ 1.55e-06 │\n",
-       "│\u001b[43m \u001b[0m\u001b[43m        R3\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m4.03e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m5.44e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m2.00e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.09e+00\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.47e+02\u001b[0m\u001b[43m \u001b[0m│\n",
-       "│ sigma_imag │ 5.06e+03 │ 4.63e+01 │ 5.06e+03 │ 4.99e+03 │ 5.14e+03 │\n",
-       "│ sigma_real │ 4.71e+03 │ 4.45e+01 │ 4.71e+03 │ 4.64e+03 │ 4.78e+03 │\n",
-       "└────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘\n"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "text/html": [
-       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                P1-R2-L3-[P4,R5], 0/1000 divergences                 </span>\n",
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                   [P1,R2]-R3, 0/1000 divergences                    </span>\n",
        "┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓\n",
        "┃<span style=\"font-weight: bold\">  Parameter </span>┃<span style=\"font-weight: bold\">     Mean </span>┃<span style=\"font-weight: bold\">      Std </span>┃<span style=\"font-weight: bold\">   Median </span>┃<span style=\"font-weight: bold\">     5.0% </span>┃<span style=\"font-weight: bold\">    95.0% </span>┃\n",
        "┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━┩\n",
-       "│<span style=\"background-color: #808000\">         L3 </span>│<span style=\"background-color: #808000\"> 3.18e-19 </span>│<span style=\"background-color: #808000\"> 1.37e-18 </span>│<span style=\"background-color: #808000\"> 2.58e-20 </span>│<span style=\"background-color: #808000\"> 4.66e-22 </span>│<span style=\"background-color: #808000\"> 1.25e-18 </span>│\n",
-       "│        P1n │ 5.55e-01 │ 1.55e-03 │ 5.55e-01 │ 5.53e-01 │ 5.58e-01 │\n",
-       "│        P1w │ 5.93e-06 │ 6.26e-08 │ 5.92e-06 │ 5.83e-06 │ 6.04e-06 │\n",
-       "│        P4n │ 8.94e-01 │ 1.29e-03 │ 8.94e-01 │ 8.92e-01 │ 8.96e-01 │\n",
-       "│        P4w │ 2.41e-06 │ 1.13e-08 │ 2.41e-06 │ 2.39e-06 │ 2.43e-06 │\n",
-       "│<span style=\"background-color: #808000\">         R2 </span>│<span style=\"background-color: #808000\"> 1.64e+01 </span>│<span style=\"background-color: #808000\"> 1.86e+01 </span>│<span style=\"background-color: #808000\"> 1.02e+01 </span>│<span style=\"background-color: #808000\"> 9.39e-01 </span>│<span style=\"background-color: #808000\"> 5.39e+01 </span>│\n",
-       "│         R5 │ 3.21e+06 │ 1.06e+04 │ 3.21e+06 │ 3.19e+06 │ 3.23e+06 │\n",
-       "│ sigma_imag │ 3.50e+03 │ 3.83e+01 │ 3.50e+03 │ 3.44e+03 │ 3.56e+03 │\n",
-       "│ sigma_real │ 5.27e+03 │ 4.39e+01 │ 5.27e+03 │ 5.20e+03 │ 5.34e+03 │\n",
+       "│        P1n │ 6.27e-01 │ 1.34e-03 │ 6.27e-01 │ 6.25e-01 │ 6.29e-01 │\n",
+       "│        P1w │ 1.44e-06 │ 4.37e-09 │ 1.44e-06 │ 1.44e-06 │ 1.45e-06 │\n",
+       "│         R2 │ 7.39e+06 │ 2.59e+04 │ 7.39e+06 │ 7.35e+06 │ 7.44e+06 │\n",
+       "│<span style=\"background-color: #808000\">         R3 </span>│<span style=\"background-color: #808000\"> 2.43e+01 </span>│<span style=\"background-color: #808000\"> 3.05e+01 </span>│<span style=\"background-color: #808000\"> 1.35e+01 </span>│<span style=\"background-color: #808000\"> 7.35e-01 </span>│<span style=\"background-color: #808000\"> 8.58e+01 </span>│\n",
+       "│ sigma_imag │ 1.11e+04 │ 7.17e+01 │ 1.11e+04 │ 1.10e+04 │ 1.12e+04 │\n",
+       "│ sigma_real │ 1.11e+04 │ 7.04e+01 │ 1.11e+04 │ 1.10e+04 │ 1.12e+04 │\n",
        "└────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘\n",
        "</pre>\n"
       ],
       "text/plain": [
-       "\u001b[3m                P1-R2-L3-[P4,R5], 0/1000 divergences                 \u001b[0m\n",
+       "\u001b[3m                   [P1,R2]-R3, 0/1000 divergences                    \u001b[0m\n",
        "┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓\n",
        "┃\u001b[1m \u001b[0m\u001b[1m Parameter\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m    Mean\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m     Std\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m  Median\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m    5.0%\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m   95.0%\u001b[0m\u001b[1m \u001b[0m┃\n",
        "┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━┩\n",
-       "│\u001b[43m \u001b[0m\u001b[43m        L3\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m3.18e-19\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.37e-18\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m2.58e-20\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m4.66e-22\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.25e-18\u001b[0m\u001b[43m \u001b[0m│\n",
-       "│        P1n │ 5.55e-01 │ 1.55e-03 │ 5.55e-01 │ 5.53e-01 │ 5.58e-01 │\n",
-       "│        P1w │ 5.93e-06 │ 6.26e-08 │ 5.92e-06 │ 5.83e-06 │ 6.04e-06 │\n",
-       "│        P4n │ 8.94e-01 │ 1.29e-03 │ 8.94e-01 │ 8.92e-01 │ 8.96e-01 │\n",
-       "│        P4w │ 2.41e-06 │ 1.13e-08 │ 2.41e-06 │ 2.39e-06 │ 2.43e-06 │\n",
-       "│\u001b[43m \u001b[0m\u001b[43m        R2\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.64e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.86e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.02e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m9.39e-01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m5.39e+01\u001b[0m\u001b[43m \u001b[0m│\n",
-       "│         R5 │ 3.21e+06 │ 1.06e+04 │ 3.21e+06 │ 3.19e+06 │ 3.23e+06 │\n",
-       "│ sigma_imag │ 3.50e+03 │ 3.83e+01 │ 3.50e+03 │ 3.44e+03 │ 3.56e+03 │\n",
-       "│ sigma_real │ 5.27e+03 │ 4.39e+01 │ 5.27e+03 │ 5.20e+03 │ 5.34e+03 │\n",
+       "│        P1n │ 6.27e-01 │ 1.34e-03 │ 6.27e-01 │ 6.25e-01 │ 6.29e-01 │\n",
+       "│        P1w │ 1.44e-06 │ 4.37e-09 │ 1.44e-06 │ 1.44e-06 │ 1.45e-06 │\n",
+       "│         R2 │ 7.39e+06 │ 2.59e+04 │ 7.39e+06 │ 7.35e+06 │ 7.44e+06 │\n",
+       "│\u001b[43m \u001b[0m\u001b[43m        R3\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m2.43e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m3.05e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.35e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m7.35e-01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m8.58e+01\u001b[0m\u001b[43m \u001b[0m│\n",
+       "│ sigma_imag │ 1.11e+04 │ 7.17e+01 │ 1.11e+04 │ 1.10e+04 │ 1.12e+04 │\n",
+       "│ sigma_real │ 1.11e+04 │ 7.04e+01 │ 1.11e+04 │ 1.10e+04 │ 1.12e+04 │\n",
        "└────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘\n"
       ]
      },
@@ -572,34 +582,32 @@
     {
      "data": {
       "text/html": [
-       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                 P1-[R2,P3]-R4, 41/1000 divergences                  </span>\n",
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                R1-[R2,[R3,P4]], 18/1000 divergences                 </span>\n",
        "┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓\n",
        "┃<span style=\"font-weight: bold\">  Parameter </span>┃<span style=\"font-weight: bold\">     Mean </span>┃<span style=\"font-weight: bold\">      Std </span>┃<span style=\"font-weight: bold\">   Median </span>┃<span style=\"font-weight: bold\">     5.0% </span>┃<span style=\"font-weight: bold\">    95.0% </span>┃\n",
        "┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━┩\n",
-       "│        P1n │ 6.68e-01 │ 5.96e-08 │ 6.68e-01 │ 6.68e-01 │ 6.68e-01 │\n",
-       "│        P1w │ 1.26e-05 │ 9.09e-13 │ 1.26e-05 │ 1.26e-05 │ 1.26e-05 │\n",
-       "│        P3n │ 8.41e-01 │ 0.00e+00 │ 8.41e-01 │ 8.41e-01 │ 8.41e-01 │\n",
-       "│        P3w │ 1.94e-06 │ 2.27e-13 │ 1.94e-06 │ 1.94e-06 │ 1.94e-06 │\n",
-       "│         R2 │ 3.85e+06 │ 0.00e+00 │ 3.85e+06 │ 3.85e+06 │ 3.85e+06 │\n",
-       "│         R4 │ 7.50e+01 │ 7.63e-06 │ 7.50e+01 │ 7.50e+01 │ 7.50e+01 │\n",
-       "│ sigma_imag │ 6.50e+00 │ 0.00e+00 │ 6.50e+00 │ 6.50e+00 │ 6.50e+00 │\n",
-       "│ sigma_real │ 1.02e+01 │ 0.00e+00 │ 1.02e+01 │ 1.02e+01 │ 1.02e+01 │\n",
+       "│        P4n │ 6.27e-01 │ 1.39e-03 │ 6.27e-01 │ 6.25e-01 │ 6.29e-01 │\n",
+       "│        P4w │ 1.44e-06 │ 4.57e-09 │ 1.44e-06 │ 1.44e-06 │ 1.45e-06 │\n",
+       "│<span style=\"background-color: #808000\">         R1 </span>│<span style=\"background-color: #808000\"> 3.16e+01 </span>│<span style=\"background-color: #808000\"> 4.20e+01 </span>│<span style=\"background-color: #808000\"> 1.58e+01 </span>│<span style=\"background-color: #808000\"> 8.14e-01 </span>│<span style=\"background-color: #808000\"> 1.40e+02 </span>│\n",
+       "│         R2 │ 7.69e+06 │ 3.79e+05 │ 7.51e+06 │ 7.38e+06 │ 8.61e+06 │\n",
+       "│<span style=\"background-color: #808000\">         R3 </span>│<span style=\"background-color: #808000\"> 1.45e+10 </span>│<span style=\"background-color: #808000\"> 1.37e+11 </span>│<span style=\"background-color: #808000\"> 5.16e+08 </span>│<span style=\"background-color: #808000\"> 5.11e+07 </span>│<span style=\"background-color: #808000\"> 2.62e+10 </span>│\n",
+       "│ sigma_imag │ 1.11e+04 │ 6.91e+01 │ 1.11e+04 │ 1.10e+04 │ 1.12e+04 │\n",
+       "│ sigma_real │ 1.11e+04 │ 6.82e+01 │ 1.11e+04 │ 1.10e+04 │ 1.13e+04 │\n",
        "└────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘\n",
        "</pre>\n"
       ],
       "text/plain": [
-       "\u001b[3m                 P1-[R2,P3]-R4, 41/1000 divergences                  \u001b[0m\n",
+       "\u001b[3m                R1-[R2,[R3,P4]], 18/1000 divergences                 \u001b[0m\n",
        "┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓\n",
        "┃\u001b[1m \u001b[0m\u001b[1m Parameter\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m    Mean\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m     Std\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m  Median\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m    5.0%\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m   95.0%\u001b[0m\u001b[1m \u001b[0m┃\n",
        "┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━┩\n",
-       "│        P1n │ 6.68e-01 │ 5.96e-08 │ 6.68e-01 │ 6.68e-01 │ 6.68e-01 │\n",
-       "│        P1w │ 1.26e-05 │ 9.09e-13 │ 1.26e-05 │ 1.26e-05 │ 1.26e-05 │\n",
-       "│        P3n │ 8.41e-01 │ 0.00e+00 │ 8.41e-01 │ 8.41e-01 │ 8.41e-01 │\n",
-       "│        P3w │ 1.94e-06 │ 2.27e-13 │ 1.94e-06 │ 1.94e-06 │ 1.94e-06 │\n",
-       "│         R2 │ 3.85e+06 │ 0.00e+00 │ 3.85e+06 │ 3.85e+06 │ 3.85e+06 │\n",
-       "│         R4 │ 7.50e+01 │ 7.63e-06 │ 7.50e+01 │ 7.50e+01 │ 7.50e+01 │\n",
-       "│ sigma_imag │ 6.50e+00 │ 0.00e+00 │ 6.50e+00 │ 6.50e+00 │ 6.50e+00 │\n",
-       "│ sigma_real │ 1.02e+01 │ 0.00e+00 │ 1.02e+01 │ 1.02e+01 │ 1.02e+01 │\n",
+       "│        P4n │ 6.27e-01 │ 1.39e-03 │ 6.27e-01 │ 6.25e-01 │ 6.29e-01 │\n",
+       "│        P4w │ 1.44e-06 │ 4.57e-09 │ 1.44e-06 │ 1.44e-06 │ 1.45e-06 │\n",
+       "│\u001b[43m \u001b[0m\u001b[43m        R1\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m3.16e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m4.20e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.58e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m8.14e-01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.40e+02\u001b[0m\u001b[43m \u001b[0m│\n",
+       "│         R2 │ 7.69e+06 │ 3.79e+05 │ 7.51e+06 │ 7.38e+06 │ 8.61e+06 │\n",
+       "│\u001b[43m \u001b[0m\u001b[43m        R3\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.45e+10\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.37e+11\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m5.16e+08\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m5.11e+07\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m2.62e+10\u001b[0m\u001b[43m \u001b[0m│\n",
+       "│ sigma_imag │ 1.11e+04 │ 6.91e+01 │ 1.11e+04 │ 1.10e+04 │ 1.12e+04 │\n",
+       "│ sigma_real │ 1.11e+04 │ 6.82e+01 │ 1.11e+04 │ 1.10e+04 │ 1.13e+04 │\n",
        "└────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘\n"
       ]
      },
@@ -609,30 +617,32 @@
     {
      "data": {
       "text/html": [
-       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                   R1-[P2,R3], 0/1000 divergences                    </span>\n",
+       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"font-style: italic\">                  R1-[P2-L3,R4], 0/1000 divergences                  </span>\n",
        "┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓\n",
        "┃<span style=\"font-weight: bold\">  Parameter </span>┃<span style=\"font-weight: bold\">     Mean </span>┃<span style=\"font-weight: bold\">      Std </span>┃<span style=\"font-weight: bold\">   Median </span>┃<span style=\"font-weight: bold\">     5.0% </span>┃<span style=\"font-weight: bold\">    95.0% </span>┃\n",
        "┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━┩\n",
-       "│        P2n │ 6.27e-01 │ 1.37e-03 │ 6.27e-01 │ 6.25e-01 │ 6.29e-01 │\n",
-       "│        P2w │ 1.44e-06 │ 4.57e-09 │ 1.44e-06 │ 1.44e-06 │ 1.45e-06 │\n",
-       "│<span style=\"background-color: #808000\">         R1 </span>│<span style=\"background-color: #808000\"> 2.31e+01 </span>│<span style=\"background-color: #808000\"> 2.89e+01 </span>│<span style=\"background-color: #808000\"> 1.30e+01 </span>│<span style=\"background-color: #808000\"> 8.10e-01 </span>│<span style=\"background-color: #808000\"> 7.65e+01 </span>│\n",
-       "│         R3 │ 7.40e+06 │ 2.64e+04 │ 7.40e+06 │ 7.35e+06 │ 7.44e+06 │\n",
-       "│ sigma_imag │ 1.11e+04 │ 7.25e+01 │ 1.11e+04 │ 1.10e+04 │ 1.12e+04 │\n",
-       "│ sigma_real │ 1.11e+04 │ 7.11e+01 │ 1.11e+04 │ 1.10e+04 │ 1.13e+04 │\n",
+       "│<span style=\"background-color: #808000\">         L3 </span>│<span style=\"background-color: #808000\"> 5.89e-20 </span>│<span style=\"background-color: #808000\"> 2.79e-19 </span>│<span style=\"background-color: #808000\"> 4.50e-21 </span>│<span style=\"background-color: #808000\"> 6.35e-23 </span>│<span style=\"background-color: #808000\"> 2.02e-19 </span>│\n",
+       "│        P2n │ 6.27e-01 │ 1.40e-03 │ 6.27e-01 │ 6.25e-01 │ 6.29e-01 │\n",
+       "│        P2w │ 1.44e-06 │ 4.50e-09 │ 1.44e-06 │ 1.44e-06 │ 1.45e-06 │\n",
+       "│<span style=\"background-color: #808000\">         R1 </span>│<span style=\"background-color: #808000\"> 2.36e+01 </span>│<span style=\"background-color: #808000\"> 2.96e+01 </span>│<span style=\"background-color: #808000\"> 1.35e+01 </span>│<span style=\"background-color: #808000\"> 1.18e+00 </span>│<span style=\"background-color: #808000\"> 7.83e+01 </span>│\n",
+       "│         R4 │ 7.39e+06 │ 2.64e+04 │ 7.39e+06 │ 7.35e+06 │ 7.43e+06 │\n",
+       "│ sigma_imag │ 1.11e+04 │ 7.03e+01 │ 1.11e+04 │ 1.10e+04 │ 1.12e+04 │\n",
+       "│ sigma_real │ 1.11e+04 │ 7.55e+01 │ 1.11e+04 │ 1.10e+04 │ 1.13e+04 │\n",
        "└────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘\n",
        "</pre>\n"
       ],
       "text/plain": [
-       "\u001b[3m                   R1-[P2,R3], 0/1000 divergences                    \u001b[0m\n",
+       "\u001b[3m                  R1-[P2-L3,R4], 0/1000 divergences                  \u001b[0m\n",
        "┏━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━┓\n",
        "┃\u001b[1m \u001b[0m\u001b[1m Parameter\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m    Mean\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m     Std\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m  Median\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m    5.0%\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m   95.0%\u001b[0m\u001b[1m \u001b[0m┃\n",
        "┡━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━┩\n",
-       "│        P2n │ 6.27e-01 │ 1.37e-03 │ 6.27e-01 │ 6.25e-01 │ 6.29e-01 │\n",
-       "│        P2w │ 1.44e-06 │ 4.57e-09 │ 1.44e-06 │ 1.44e-06 │ 1.45e-06 │\n",
-       "│\u001b[43m \u001b[0m\u001b[43m        R1\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m2.31e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m2.89e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.30e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m8.10e-01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m7.65e+01\u001b[0m\u001b[43m \u001b[0m│\n",
-       "│         R3 │ 7.40e+06 │ 2.64e+04 │ 7.40e+06 │ 7.35e+06 │ 7.44e+06 │\n",
-       "│ sigma_imag │ 1.11e+04 │ 7.25e+01 │ 1.11e+04 │ 1.10e+04 │ 1.12e+04 │\n",
-       "│ sigma_real │ 1.11e+04 │ 7.11e+01 │ 1.11e+04 │ 1.10e+04 │ 1.13e+04 │\n",
+       "│\u001b[43m \u001b[0m\u001b[43m        L3\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m5.89e-20\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m2.79e-19\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m4.50e-21\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m6.35e-23\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m2.02e-19\u001b[0m\u001b[43m \u001b[0m│\n",
+       "│        P2n │ 6.27e-01 │ 1.40e-03 │ 6.27e-01 │ 6.25e-01 │ 6.29e-01 │\n",
+       "│        P2w │ 1.44e-06 │ 4.50e-09 │ 1.44e-06 │ 1.44e-06 │ 1.45e-06 │\n",
+       "│\u001b[43m \u001b[0m\u001b[43m        R1\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m2.36e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m2.96e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.35e+01\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m1.18e+00\u001b[0m\u001b[43m \u001b[0m│\u001b[43m \u001b[0m\u001b[43m7.83e+01\u001b[0m\u001b[43m \u001b[0m│\n",
+       "│         R4 │ 7.39e+06 │ 2.64e+04 │ 7.39e+06 │ 7.35e+06 │ 7.43e+06 │\n",
+       "│ sigma_imag │ 1.11e+04 │ 7.03e+01 │ 1.11e+04 │ 1.10e+04 │ 1.12e+04 │\n",
+       "│ sigma_real │ 1.11e+04 │ 7.55e+01 │ 1.11e+04 │ 1.10e+04 │ 1.13e+04 │\n",
        "└────────────┴──────────┴──────────┴──────────┴──────────┴──────────┘\n"
       ]
      },
diff --git a/pyproject.toml b/pyproject.toml
index 196ee8be..2ed5ce63 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -38,6 +38,8 @@ dependencies = [
     "ipywidgets",
     "jax",
     "julia",
+    "juliacall",
+    "juliapkg",
     "matplotlib",
     "mpire[dill]",
     "numpy",


Test patch:
diff --git a/tests/test_backend.py b/tests/test_backend.py
index bf598f1e..a8675351 100644
--- a/tests/test_backend.py
+++ b/tests/test_backend.py
@@ -5,15 +5,29 @@
 
 def test_init_julia():
     Main = julia_helpers.init_julia()
-    assert Main.eval("1+1") == 2
+    assert Main.seval("1+1") == 2
 
 
 def test_import_julia_modules():
     Main = julia_helpers.init_julia()
 
     # Ensure installed modules can be imported
-    ec = julia_helpers.import_package("EquivalentCircuits", Main=Main)
+    ec = julia_helpers.import_package("EquivalentCircuits", Main)
+    assert hasattr(ec, "circuit_evolution")
 
-    # Throw error for non-existent module
+    # Throw error for non-existent module if error=True
     with pytest.raises(Exception):
-        julia_helpers.import_package("NonExistentModule", Main)
+        julia_helpers.import_package("NonExistentModule", Main, error=True)
+    # Otherwise, return None
+    ref = julia_helpers.import_package("NonExistentModule", Main, error=False)
+    assert ref is None
+
+
+def test_import_backend():
+    # Import backend with Julia runtime as argument
+    Main = julia_helpers.init_julia()
+    ec = julia_helpers.import_backend(Main)
+    assert hasattr(ec, "circuit_evolution")
+    # Import backend without Julia runtime as argument
+    ec = julia_helpers.import_backend()
+    assert hasattr(ec, "circuit_evolution")
diff --git a/tests/test_core.py b/tests/test_core.py
index 5dc110dd..32a49369 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -36,12 +36,13 @@ def test_gep_serial():
     kwargs = {
         "iters": 2,
         "complexity": 12,
-        "population_size": 10,
+        "population_size": 5,
         "generations": 5,
-        "tol": 1e-2,
+        "tol": 1e10,
         "parallel": False,
     }
     circuits = core.generate_equivalent_circuits(Z, freq, **kwargs)
+    assert len(circuits) == kwargs["iters"]
     assert isinstance(circuits, pd.DataFrame)
 
 
@@ -51,12 +52,13 @@ def test_gep_parallel():
     kwargs = {
         "iters": 2,
         "complexity": 12,
-        "population_size": 10,
+        "population_size": 5,
         "generations": 5,
-        "tol": 1e-2,
+        "tol": 1e10,
         "parallel": True,
     }
     circuits = core.generate_equivalent_circuits(Z, freq, **kwargs)
+    assert len(circuits) == kwargs["iters"]
     assert isinstance(circuits, pd.DataFrame)