""" A set of utility functions to help the Collections """ import math from typing import Any, Dict, List __registered_collections = {} def nCr(n: int, r: int) -> int: """ Compute the binomial coefficient n! / (k! * (n-k)!) Parameters ---------- n : int Number of samples r : int Denominator Returns ------- ret : int Value """ return math.factorial(n) / math.factorial(r) / math.factorial(n - r) def register_collection(collection: "Collection") -> None: """Registers a collection for use by the factory. Parameters ---------- collection : Collection The Collection class to be registered """ class_name = collection.__name__.lower() # if class_name in __registered_collections: # raise KeyError("Collection type '{}' already registered".format(class_name)) __registered_collections[class_name] = collection def collection_factory(data: Dict[str, Any], client: "FractalClient" = None) -> "Collection": """Creates a new Collection class from a JSON blob. Parameters ---------- data : Dict[str, Any] The JSON blob to create a new class from. client : FractalClient, optional A FractalClient connected to a server Returns ------- Collection A ODM of the data. """ if "collection" not in data: raise KeyError("Attempted to create Collection from JSON, but no `collection` field found.") if data["collection"].lower() not in __registered_collections: raise KeyError("Attempted to create Collection of unknown type '{}'.".format(data["collection"])) return __registered_collections[data["collection"].lower()].from_json(data, client=client) def collections_name_map() -> Dict[str, str]: """ Returns a map of internal name to external Collection name. Returns ------- Dict[str, str] Map of {'internal': 'user fiendly name'} """ return {k: v.__name__ for k, v in __registered_collections.items()} def list_known_collections() -> List[str]: """ Returns the case sensitive list of Collection names. Returns ------- List[str] A list of registered collections """ return list(collection_name_map.values()) def composition_planner(program=None, method=None, basis=None, driver=None, keywords=None): """ Plans out a given query into multiple pieces """ base = {"program": program, "method": method, "basis": basis, "driver": driver, "keywords": keywords} if ("-d3" in method.lower()) and ("dftd3" != program.lower()) and ("hessian" != driver.lower()): dftd3keys = {"program": "dftd3", "method": method, "basis": None, "driver": driver, "keywords": None} base["method"] = method.lower().split("-d3")[0] return [dftd3keys, base] else: return [base]