{ "cells": [ { "cell_type": "markdown", "id": "0577f61d-635c-436c-96ca-1471265ac1fb", "metadata": {}, "source": [ "# Exploration of formulas in open-source ECC libraries\n", "\n", "This notebook explores formulas found in open-source ECC libraries.\n", "\n", " - [Analysis](#Analysis)\n", " - [Formulas](#The-formulas)\n", " - [Expand](#Expand) " ] }, { "cell_type": "code", "id": "397771ed-7408-47a6-8b82-75e047e100fe", "metadata": {}, "source": [ "import itertools\n", "import numpy as np\n", "import inspect\n", "import tempfile\n", "import sys\n", "import multiprocessing\n", "\n", "from copy import deepcopy\n", "from concurrent.futures import ProcessPoolExecutor, as_completed\n", "from contextlib import contextmanager\n", "from importlib import import_module, invalidate_caches\n", "from pathlib import Path\n", "\n", "from matplotlib import pyplot as plt\n", "from tqdm.notebook import tqdm\n", "from importlib_resources import files\n", "\n", "import pyecsca.ec\n", "from pyecsca.ec.model import ShortWeierstrassModel, TwistedEdwardsModel, MontgomeryModel\n", "from pyecsca.ec.formula import AdditionEFDFormula, DoublingEFDFormula, LadderEFDFormula\n", "from pyecsca.ec.params import get_params\n", "from pyecsca.ec.formula.metrics import formula_similarity, formula_similarity_fuzz\n", "\n", "\n", "# Allow to use \"spawn\" multiprocessing method for function defined in a Jupyter notebook.\n", "# https://neuromancer.sk/article/35\n", "@contextmanager\n", "def enable_spawn(func):\n", " invalidate_caches()\n", " source = inspect.getsource(func)\n", " with tempfile.NamedTemporaryFile(suffix=\".py\", mode=\"w\") as f:\n", " f.write(source)\n", " f.flush()\n", " path = Path(f.name)\n", " directory = str(path.parent)\n", " sys.path.append(directory)\n", " module = import_module(str(path.stem))\n", " yield getattr(module, func.__name__)\n", " sys.path.remove(directory)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "1727bd9a-f209-4d91-9286-8cca9118c187", "metadata": {}, "source": [ "sw = ShortWeierstrassModel()\n", "mont = MontgomeryModel()\n", "te = TwistedEdwardsModel()\n", "\n", "curve25519 = get_params(\"other\", \"Curve25519\", \"xz\")\n", "ed25519 = get_params(\"other\", \"Ed25519\", \"extended\")\n", "p256_jac3 = get_params(\"secg\", \"secp256r1\", \"jacobian-3\")\n", "p256_jac = get_params(\"secg\", \"secp256r1\", \"jacobian\")\n", "p256_mod = get_params(\"secg\", \"secp256r1\", \"modified\")\n", "p256_proj3 = get_params(\"secg\", \"secp256r1\", \"projective-3\")" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "id": "0d686ad1-4b20-4327-95be-36d6e92052b3", "metadata": {}, "source": [ "## The formulas\n", "The following formulas were collected from open-source cryptographic libraries and are stored in the core package of the **pyecsca** toolkit. For a full overview of implementation choices done by open-source ECC libraries, see [this page](/libraries.rst)." ] }, { "cell_type": "code", "id": "21572b7d-380c-4570-b5bd-2043306adc3e", "metadata": {}, "source": [ "lib_formula_defs = [\n", " [\n", " \"add-bc-r1rv76-jac\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian\",\n", " (\"secg\", \"secp128r1\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"add-bc-r1rv76-mod\", #ok\n", " ShortWeierstrassModel,\n", " \"modified\",\n", " (\"secg\", \"secp128r1\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"dbl-bc-r1rv76-jac\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian\",\n", " (\"secg\", \"secp128r1\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"dbl-bc-r1rv76-mod\", #ok\n", " ShortWeierstrassModel,\n", " \"modified\",\n", " (\"secg\", \"secp128r1\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"dbl-bc-r1rv76-x25519\", #ok\n", " MontgomeryModel,\n", " \"xz\",\n", " (\"other\", \"Curve25519\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"ladd-bc-r1rv76-x25519\", #ok\n", " MontgomeryModel,\n", " \"xz\",\n", " (\"other\", \"Curve25519\"),\n", " LadderEFDFormula,\n", " ],\n", " [\n", " \"dbl-boringssl-p224\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian-3\",\n", " (\"secg\", \"secp224r1\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"add-boringssl-p224\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian-3\",\n", " (\"secg\", \"secp224r1\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"add-libressl-v382\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian\",\n", " (\"secg\", \"secp128r1\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"dbl-libressl-v382\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian\",\n", " (\"secg\", \"secp128r1\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"dbl-secp256k1-v040\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian\",\n", " (\"secg\", \"secp256k1\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"add-openssl-z256\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian-3\",\n", " (\"secg\", \"secp256r1\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"add-openssl-z256a\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian-3\",\n", " (\"secg\", \"secp256r1\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"ladd-openssl-x25519\", #ok\n", " MontgomeryModel,\n", " \"xz\",\n", " (\"other\", \"Curve25519\"),\n", " LadderEFDFormula,\n", " ],\n", " [\n", " \"ladd-hacl-x25519\", #ok\n", " MontgomeryModel,\n", " \"xz\",\n", " (\"other\", \"Curve25519\"),\n", " LadderEFDFormula,\n", " ],\n", " [\n", " \"dbl-hacl-x25519\", #ok\n", " MontgomeryModel,\n", " \"xz\",\n", " (\"other\", \"Curve25519\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"dbl-sunec-v21\", #ok\n", " ShortWeierstrassModel,\n", " \"projective-3\",\n", " (\"secg\", \"secp256r1\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"add-sunec-v21\", #ok\n", " ShortWeierstrassModel,\n", " \"projective-3\",\n", " (\"secg\", \"secp256r1\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"add-sunec-v21-ed25519\", #ok\n", " TwistedEdwardsModel,\n", " \"extended-1\",\n", " (\"other\", \"Ed25519\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"dbl-sunec-v21-ed25519\", #ok\n", " TwistedEdwardsModel,\n", " \"extended-1\",\n", " (\"other\", \"Ed25519\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"ladd-rfc7748\", #ok\n", " MontgomeryModel,\n", " \"xz\",\n", " (\"other\", \"Curve25519\"),\n", " LadderEFDFormula,\n", " ],\n", " [\n", " \"add-bearssl-v06\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian\",\n", " (\"secg\", \"secp256r1\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"dbl-bearssl-v06\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian\",\n", " (\"secg\", \"secp256r1\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"add-libgcrypt-v1102\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian\",\n", " (\"secg\", \"secp256r1\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"dbl-libgcrypt-v1102\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian\",\n", " (\"secg\", \"secp256r1\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"ladd-go-1214\", #ok\n", " MontgomeryModel,\n", " \"xz\",\n", " (\"other\", \"Curve25519\"),\n", " LadderEFDFormula,\n", " ],\n", " [\n", " \"add-gecc-322\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian-3\",\n", " (\"secg\", \"secp256r1\"),\n", " AdditionEFDFormula,\n", " ],\n", " [\n", " \"dbl-gecc-321\", #ok\n", " ShortWeierstrassModel,\n", " \"jacobian-3\",\n", " (\"secg\", \"secp256r1\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"ladd-boringssl-x25519\", #ok\n", " MontgomeryModel,\n", " \"xz\",\n", " (\"other\", \"Curve25519\"),\n", " LadderEFDFormula,\n", " ],\n", " [\n", " \"dbl-ipp-x25519\", #ok\n", " MontgomeryModel,\n", " \"xz\",\n", " (\"other\", \"Curve25519\"),\n", " DoublingEFDFormula,\n", " ],\n", " [\n", " \"ladd-botan-x25519\", #ok\n", " MontgomeryModel,\n", " \"xz\",\n", " (\"other\", \"Curve25519\"),\n", " LadderEFDFormula,\n", " ],\n", "]" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "id": "176fcb8c-e725-4e3b-9835-729cfb795206", "metadata": {}, "source": [ "Let's load the formulas now." ] }, { "cell_type": "code", "id": "0cfcbf60-7d6b-4ab0-b957-e998037ff8ab", "metadata": {}, "source": [ "lib_formulas = {}\n", "base_path = files(pyecsca.ec).joinpath(\"data\", \"formulas\")\n", "for formula_def in lib_formula_defs:\n", " meta_path = base_path / formula_def[0]\n", " op3_path = base_path / (formula_def[0] + \".op3\")\n", " model = formula_def[1]()\n", " formula = formula_def[4](meta_path, op3_path, formula_def[0], model.coordinates[formula_def[2]]).to_code()\n", " lib_formulas[formula_def[0]] = formula" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "74002746-f3f3-4528-9042-b9ab51791bc2", "metadata": {}, "source": [ "len(lib_formulas)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "id": "5800e043-85bc-4fac-8c40-75c234beec1c", "metadata": {}, "source": [ "Now we can set up some code for examining the similarities between the formulas. This is done in two ways:\n", "\n", " - Measure similarity between sets of intermediate polynomials of the formulas (like in the **pyecsca** paper),\n", " - Measure similarity between sets of intermediate values of the formulas over random points on some curve (the *fuzz* approach)." ] }, { "cell_type": "code", "id": "e0a91c61-1565-49bd-9604-5a371710f91a", "metadata": {}, "source": [ "def compute_similarities(formulas, curve):\n", " table = [[\"One\", \"Other\", \"Similarity (output)\", \"Similarity (IV)\"]]\n", "\n", " im_iv = np.zeros((len(formulas), len(formulas)))\n", " im_out = np.zeros((len(formulas), len(formulas)))\n", " for formula in formulas:\n", " if formula.assumptions:\n", " for assumption_str in formula.assumptions_str:\n", " lhs, rhs = assumption_str.strip().split(\" == \")\n", " if lhs in formula.inputs:\n", " print(f\"Warning, formula {formula.name} has assumptions: {assumption_str}\")\n", " for one, other in itertools.product(formulas, formulas):\n", " i = formulas.index(one)\n", " j = formulas.index(other)\n", " if curve is None:\n", " sim = formula_similarity(one, other)\n", " else:\n", " sim = formula_similarity_fuzz(one, other, curve, 100)\n", " im_iv[i, j] = sim[\"ivs\"]\n", " im_out[i, j] = sim[\"output\"]\n", " table.append([one.name, other.name, f\"{sim['output']:.2}\", f\"{sim['ivs']:.2}\"])\n", " \n", " return table, im_iv, im_out\n", "\n", "def plot_similarities(formulas, im_data, name):\n", " fig, ax = plt.subplots()\n", " im = ax.imshow(im_data, vmin=0)\n", " cbar_ax = fig.add_axes((0.90, 0.11, 0.04, 0.76))\n", " cbar = fig.colorbar(im, cax=cbar_ax)\n", " cbar.ax.set_ylabel(f\"Similarity ({name})\", rotation=-90, va=\"bottom\")\n", " \n", " ax.set_xticks(np.arange(len(formulas)), labels=list(map(lambda f: f.name, formulas)), rotation=90)\n", " ax.set_yticks(np.arange(len(formulas)), labels=list(map(lambda f: f.name, formulas)), rotation=0)\n", " \n", " for i, one in enumerate(formulas):\n", " for j, other in enumerate(formulas):\n", " ax.text(j, i, f\"{im_data[i, j]:.2}\", ha=\"center\", va=\"center\", color=\"white\")\n", " plt.show()\n", "\n", "def analyze_formulas(formulas, curve=None):\n", " table, im_iv, im_out = compute_similarities(formulas, curve)\n", " plot_similarities(formulas, im_iv, \"IV\")\n", " plot_similarities(formulas, im_out, \"output\")" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "id": "392d7eea-abcb-4d83-82b4-a9394d3d44aa", "metadata": {}, "source": [ "## Analysis\n", "\n", "Let's now go through the library formulas grouped by their type and coordinate model." ] }, { "cell_type": "code", "id": "932e8ec8-77f6-4079-909c-3ca021fde717", "metadata": {}, "source": [ "xz_ladders = [formula for formula in mont.coordinates[\"xz\"].formulas.values() if formula.name.startswith(\"ladd\") or formula.name.startswith(\"mladd\")] + [\n", " lib_formulas[\"ladd-rfc7748\"], lib_formulas[\"ladd-hacl-x25519\"], lib_formulas[\"ladd-openssl-x25519\"], lib_formulas[\"ladd-bc-r1rv76-x25519\"],\n", " lib_formulas[\"ladd-go-1214\"], lib_formulas[\"ladd-boringssl-x25519\"], lib_formulas[\"ladd-botan-x25519\"]]\n", "analyze_formulas(xz_ladders)\n", "analyze_formulas(xz_ladders, curve25519.curve)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "f2e0e3b5-b7e8-4ed7-94a1-a74976b108fa", "metadata": {}, "source": [ "xz_dbls = [formula for formula in mont.coordinates[\"xz\"].formulas.values() if formula.name.startswith(\"dbl\")] + [\n", " lib_formulas[\"dbl-bc-r1rv76-x25519\"], lib_formulas[\"dbl-hacl-x25519\"], lib_formulas[\"dbl-ipp-x25519\"]]\n", "analyze_formulas(xz_dbls)\n", "analyze_formulas(xz_dbls, curve25519.curve)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "724791a0-4137-4d3e-abaa-a90e4e2187ec", "metadata": {}, "source": [ "jac3_adds = [formula for formula in sw.coordinates[\"jacobian-3\"].formulas.values() if formula.name.startswith(\"add\") or formula.name.startswith(\"madd\")] + [\n", " lib_formulas[\"add-boringssl-p224\"], lib_formulas[\"add-openssl-z256\"], lib_formulas[\"add-openssl-z256a\"],\n", " lib_formulas[\"add-gecc-322\"]]\n", "jac3_adds_fixed = []\n", "for formula in jac3_adds:\n", " if formula.coordinate_model != sw.coordinates[\"jacobian-3\"]:\n", " formula = deepcopy(formula)\n", " formula.coordinate_model = sw.coordinates[\"jacobian-3\"]\n", " jac3_adds_fixed.append(formula)\n", "analyze_formulas(jac3_adds_fixed)\n", "analyze_formulas(jac3_adds_fixed, p256_jac3.curve)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "d12b98bc-71b5-4800-8762-ca87eb02d050", "metadata": {}, "source": [ "jac3_dbls = [formula for formula in sw.coordinates[\"jacobian-3\"].formulas.values() if formula.name.startswith(\"dbl\")] + [\n", " lib_formulas[\"dbl-boringssl-p224\"], lib_formulas[\"dbl-gecc-321\"]]\n", "jac3_dbls_fixed = []\n", "for formula in jac3_dbls:\n", " if formula.coordinate_model != sw.coordinates[\"jacobian-3\"]:\n", " formula = deepcopy(formula)\n", " formula.coordinate_model = sw.coordinates[\"jacobian-3\"]\n", " jac3_dbls_fixed.append(formula)\n", "analyze_formulas(jac3_dbls_fixed)\n", "analyze_formulas(jac3_dbls_fixed, p256_jac3.curve)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "ae0cde46-061c-48f1-9fb6-67a2798c5068", "metadata": {}, "source": [ "mod_adds = [formula for formula in sw.coordinates[\"modified\"].formulas.values() if formula.name.startswith(\"add\") or formula.name.startswith(\"madd\")] + [\n", " lib_formulas[\"add-bc-r1rv76-mod\"]]\n", "analyze_formulas(mod_adds)\n", "analyze_formulas(mod_adds, p256_mod.curve)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "c7226513-9866-405f-9f4e-ad83134b957a", "metadata": {}, "source": [ "mod_dbls = [formula for formula in sw.coordinates[\"modified\"].formulas.values() if formula.name.startswith(\"dbl\")] + [\n", " lib_formulas[\"dbl-bc-r1rv76-mod\"]]\n", "analyze_formulas(mod_dbls)\n", "analyze_formulas(mod_dbls, p256_mod.curve)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "313570ca-082e-46df-8420-ed47dc78855b", "metadata": {}, "source": [ "jac_adds = [formula for formula in sw.coordinates[\"jacobian\"].formulas.values() if formula.name.startswith(\"add\")] + [\n", " lib_formulas[\"add-bc-r1rv76-jac\"], lib_formulas[\"add-libressl-v382\"], lib_formulas[\"add-bearssl-v06\"], lib_formulas[\"add-libgcrypt-v1102\"]]\n", "analyze_formulas(jac_adds)\n", "analyze_formulas(jac_adds, p256_jac.curve)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "9a0e3899-872d-411b-8778-362e464390df", "metadata": {}, "source": [ "jac_dbls = [formula for formula in sw.coordinates[\"jacobian\"].formulas.values() if formula.name.startswith(\"dbl\")] + [\n", " lib_formulas[\"dbl-secp256k1-v040\"], lib_formulas[\"dbl-libressl-v382\"], lib_formulas[\"dbl-bearssl-v06\"], lib_formulas[\"dbl-libgcrypt-v1102\"]]\n", "analyze_formulas(jac_dbls)\n", "analyze_formulas(jac_dbls, p256_jac.curve)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "881c383e-3062-4f1e-98e3-adc336749a20", "metadata": {}, "source": [ "proj3_adds = [formula for formula in sw.coordinates[\"projective-3\"].formulas.values() if formula.name.startswith(\"add\") or formula.name.startswith(\"madd\")] + [\n", " lib_formulas[\"add-sunec-v21\"]]\n", "analyze_formulas(proj3_adds)\n", "analyze_formulas(proj3_adds, p256_proj3.curve)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "d34e20a4-bf1b-4245-bbb2-a53fdd3446a3", "metadata": {}, "source": [ "proj3_dbls = [formula for formula in sw.coordinates[\"projective-3\"].formulas.values() if formula.name.startswith(\"dbl\")] + [\n", " lib_formulas[\"dbl-sunec-v21\"]]\n", "analyze_formulas(proj3_dbls)\n", "analyze_formulas(proj3_dbls, p256_proj3.curve)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "78f7ff19-9f97-4459-b31e-0d7efe3b7dfa", "metadata": {}, "source": [ "ext_adds = [formula for formula in te.coordinates[\"extended-1\"].formulas.values() if formula.name.startswith(\"add\") or formula.name.startswith(\"madd\")] + [\n", " lib_formulas[\"add-sunec-v21-ed25519\"]]\n", "analyze_formulas(ext_adds)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "fb60bd5f-47a6-4a8f-9864-66bc750bfe97", "metadata": {}, "source": [ "ext_dbls = [formula for formula in te.coordinates[\"extended-1\"].formulas.values() if formula.name.startswith(\"dbl\")] + [\n", " lib_formulas[\"dbl-sunec-v21-ed25519\"]]\n", "analyze_formulas(ext_dbls)" ], "outputs": [], "execution_count": null }, { "cell_type": "markdown", "id": "39f642c2-2350-4310-b365-3be6c3f6fcdb", "metadata": {}, "source": [ "## Expand\n", "We can also expand the set of formulas by applying various transformations (like swapping the order of commutative operations).\n", "\n", "
\n", "\n", "This is a resource intensive cell that uses parallelism. Set the `num_workers` variable to something reasonable, like the number of cores of your machine minus two.\n", "\n", "
" ] }, { "metadata": {}, "cell_type": "code", "source": [ "# 22 is really a maximum usable number here, as there are only 22 \"jobs\".\n", "num_workers = 22" ], "id": "6cc916235ef2e061", "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "f3c2c134-a42d-4bb2-b729-0ec1ddcbcdef", "metadata": {}, "source": [ "def expand_out(formulas, name):\n", " from pyecsca.ec.formula.expand import expand_formula_set\n", " import pickle\n", "\n", " expanded = expand_formula_set(formulas)\n", " with open(name, \"wb\") as f:\n", " pickle.dump(expanded, f)\n", " return name, len(expanded)\n", "\n", "context = multiprocessing.get_context(\"spawn\")\n", "with ProcessPoolExecutor(max_workers=num_workers, mp_context=context) as pool, enable_spawn(expand_out) as expand_func:\n", " futures = []\n", " args = []\n", " for coord_name, coords in tqdm(sw.coordinates.items(), desc=\"Submitting\"):\n", " adds = [formula for formula in coords.formulas.values() if formula.name.startswith(\"add\")]\n", " lib_adds = [formula for formula in lib_formulas.values() if formula.coordinate_model == coords and formula.name.startswith(\"add\")]\n", " dbls = [formula for formula in coords.formulas.values() if formula.name.startswith(\"dbl\")]\n", " lib_dbls = [formula for formula in lib_formulas.values() if formula.coordinate_model == coords and formula.name.startswith(\"dbl\")]\n", " futures.append(pool.submit(expand_func, adds + lib_adds, f\"sw_{coord_name}_adds.pickle\"))\n", " args.append(f\"sw_{coord_name}_adds.pickle\")\n", " futures.append(pool.submit(expand_func, dbls + lib_dbls, f\"sw_{coord_name}_dbls.pickle\"))\n", " args.append(f\"sw_{coord_name}_dbls.pickle\")\n", " for future in tqdm(as_completed(futures), total=len(futures), smoothing=0, desc=\"Computing\"):\n", " j = futures.index(future)\n", " arg = args[j]\n", " error = future.exception()\n", " if error:\n", " print(arg, error)\n", " else:\n", " res = future.result()\n", " print(*res)" ], "outputs": [], "execution_count": null }, { "cell_type": "code", "id": "a5b1ccd8-1b2b-4e1a-85e1-5cab2b45300c", "metadata": {}, "source": [], "outputs": [], "execution_count": null } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.4" } }, "nbformat": 4, "nbformat_minor": 5 }