diff --git a/cosmopower/cosmopower_NN.py b/cosmopower/cosmopower_NN.py index 94e089d..b74dfc0 100644 --- a/cosmopower/cosmopower_NN.py +++ b/cosmopower/cosmopower_NN.py @@ -118,22 +118,32 @@ def __init__(self, parameters: Optional[Sequence[str]] = None, self.alphas = [] self.betas = [] for i in range(self.n_layers): - self.W.append(tf.Variable( - tf.random.normal([ - self.architecture[i], self.architecture[i + 1] - ], 0.0, 1e-3), - name="W_" + str(i), trainable=trainable)) - self.b.append(tf.Variable(tf.zeros([self.architecture[i + 1]]), - name="b_" + str(i), - trainable=trainable)) + self.W.append(self.add_weight( + initializer=tf.keras.initializers.RandomNormal(stddev=1e-3), + shape=(self.architecture[i], self.architecture[i + 1]), + name="W_" + str(i), + trainable=trainable + )) + self.b.append(self.add_weight( + initializer="zeros", + shape=(self.architecture[i + 1],), + name="b_" + str(i), + trainable=trainable + )) for i in range(self.n_layers - 1): - self.alphas.append(tf.Variable( - tf.random.normal([self.architecture[i + 1]]), - name="alphas_" + str(i), trainable=trainable)) - self.betas.append(tf.Variable( - tf.random.normal([self.architecture[i + 1]]), - name="betas_" + str(i), trainable=trainable)) + self.alphas.append(self.add_weight( + initializer="random_normal", + shape=(self.architecture[i + 1],), + name="alphas_" + str(i), + trainable=trainable + )) + self.betas.append(self.add_weight( + initializer="random_normal", + shape=(self.architecture[i + 1],), + name="betas_" + str(i), + trainable=trainable + )) # restore weights if restoring if restore_filename is not None: @@ -546,7 +556,6 @@ def ten_to_rescaled_predictions_np(self, parameters_dict: dict return 10.**self.rescaled_predictions_np(parameters_dict) # Infrastructure for network training - @tf.function def compute_loss(self, training_parameters: tf.Tensor, training_features: tf.Tensor) -> tf.Tensor: @@ -595,7 +604,6 @@ def compute_loss_and_gradients(self, """ # compute loss on the tape with tf.GradientTape() as tape: - # loss loss = tf.sqrt( tf.reduce_mean( @@ -888,7 +896,6 @@ def train(self, training_data: Sequence, for epoch in t: # loop over batches for theta, feats in training_data: - # training step: check whether to accumulate gradients # or not (only worth doing this for very large batch # sizes) diff --git a/cosmopower/cosmopower_PCAplusNN.py b/cosmopower/cosmopower_PCAplusNN.py index abc8c4b..e831282 100644 --- a/cosmopower/cosmopower_PCAplusNN.py +++ b/cosmopower/cosmopower_PCAplusNN.py @@ -128,21 +128,32 @@ def __init__(self, cp_pca: Optional[object] = None, self.alphas = [] self.betas = [] for i in range(self.n_layers): - self.W.append(tf.Variable( - tf.random.normal([ - self.architecture[i], self.architecture[i + 1] - ], 0.0, np.sqrt(2.0 / self.n_parameters)), - name="W_" + str(i), trainable=trainable)) - self.b.append(tf.Variable(tf.zeros([self.architecture[i + 1]]), - name="b_" + str(i), - trainable=trainable)) + self.W.append(self.add_weight( + initializer=tf.keras.initializers.RandomNormal(stddev=1e-3), + shape=(self.architecture[i], self.architecture[i + 1]), + name="W_" + str(i), + trainable=trainable + )) + self.b.append(self.add_weight( + initializer="zeros", + shape=(self.architecture[i + 1],), + name="b_" + str(i), + trainable=trainable + )) + for i in range(self.n_layers - 1): - self.alphas.append(tf.Variable( - tf.random.normal([self.architecture[i + 1]]), - name="alphas_" + str(i), trainable=trainable)) - self.betas.append(tf.Variable( - tf.random.normal([self.architecture[i + 1]]), - name="betas_" + str(i), trainable=trainable)) + self.alphas.append(self.add_weight( + initializer="random_normal", + shape=(self.architecture[i + 1],), + name="alphas_" + str(i), + trainable=trainable + )) + self.betas.append(self.add_weight( + initializer="random_normal", + shape=(self.architecture[i + 1],), + name="betas_" + str(i), + trainable=trainable + )) # restore weights if restoring if restore_filename is not None: @@ -923,8 +934,8 @@ def train(self, training_data: Sequence, ).shuffle(n_training).batch(batch_sizes[i]) # set up training loss - validation_loss = [np.infty] - best_loss = np.infty + validation_loss = [np.inf] + best_loss = np.inf early_stopping_counter = 0 # loop over epochs diff --git a/cosmopower/dataset.py b/cosmopower/dataset.py index 76b75f6..f86cd0d 100644 --- a/cosmopower/dataset.py +++ b/cosmopower/dataset.py @@ -170,7 +170,7 @@ def read_parameters(self, indices: Union[int, np.ndarray]) -> dict: cast_1d = (type(indices) is int) indices = np.atleast_1d(indices) - entries = np.where(np.in1d(self.file["data"]["indices"][:], + entries = np.where(np.isin(self.file["data"]["indices"][:], indices))[0] parameters = { @@ -179,7 +179,7 @@ def read_parameters(self, indices: Union[int, np.ndarray]) -> dict: } if cast_1d: - parameters = {k: float(v) for k, v in parameters.items()} + parameters = {k: float(v[0]) for k, v in parameters.items()} return parameters @@ -193,7 +193,7 @@ def read_spectra(self, indices: Union[int, np.ndarray]) -> np.ndarray: cast_1d = (type(indices) is int) indices = np.atleast_1d(indices) - entries = np.where(np.in1d(self.file["data"]["indices"][:], + entries = np.where(np.isin(self.file["data"]["indices"][:], indices))[0] spec = self.file["data"]["spectra"][entries, :] if cast_1d: diff --git a/cosmopower/examples/1_create_training_data.py b/cosmopower/examples/1_create_training_data.py index ce695c0..981f5b3 100644 --- a/cosmopower/examples/1_create_training_data.py +++ b/cosmopower/examples/1_create_training_data.py @@ -2,7 +2,7 @@ # Author: A. Spurio Mancini, H. T. Jense -import tqdm +from tqdm import tqdm import numpy as np from cosmopower.parser import YAMLParser from cosmopower.spectra import init_boltzmann_code, get_boltzmann_spectra @@ -51,7 +51,8 @@ samples, validation_samples, testing_samples = \ parser.get_parameter_samples() -print(samples) +for k in samples: + print(k, samples[k][:10]) parser.save_samples_to_file(samples, validation_samples) @@ -128,7 +129,7 @@ # This function returns False if the sample is invalid, so it's a # simple check to discard invalid datapoints. network_params = np.array([ - params[k] + samples[k][n] for k in parser.network_input_parameters("Pk/lin") ]) dataset.write_data(n, network_params, @@ -147,7 +148,7 @@ # This function returns False if the sample is invalid, so it's a # simple check to discard invalid datapoints. network_params = np.array([ - params[k] + samples[k][n] for k in parser.network_input_parameters("Pk/lin") ]) dataset.write_data(n, network_params, @@ -163,7 +164,7 @@ # This function returns False if the sample is invalid, so it's a # simple check to discard invalid datapoints. network_params = np.array([ - params[k] + samples[k][n] for k in parser.network_input_parameters("Pk/lin") ]) dataset.write_data(n, network_params, diff --git a/cosmopower/examples/2b_train_PCA_emulator.py b/cosmopower/examples/2b_train_PCA_emulator.py index 1cf09ce..f3b71ea 100644 --- a/cosmopower/examples/2b_train_PCA_emulator.py +++ b/cosmopower/examples/2b_train_PCA_emulator.py @@ -42,7 +42,7 @@ network = cosmopower_PCAplusNN(cp_pca=pca, verbose=True, trainable=True, **settings.get("n_traits", {})) -with tf.device("/device:CPU:0"): +with tf.device(None): network.train(training_data=datasets, filename_saved_model=output_file, validation=validation, diff --git a/cosmopower/examples/example.yaml b/cosmopower/examples/example.yaml index 4ba920b..9dd3e26 100644 --- a/cosmopower/examples/example.yaml +++ b/cosmopower/examples/example.yaml @@ -4,7 +4,7 @@ path: example emulated_code: name: camb # which parameters are passed to camb - inputs: [ombh2, omch2, H0, ns, As] + inputs: [ombh2, omch2, H0, ns, As, z] extra_args: # Some extra accuracy and speed parameters kmax: 20.0 diff --git a/cosmopower/examples/planck_cmb.yaml b/cosmopower/examples/planck_cmb.yaml index d0f30a4..b1493b0 100644 --- a/cosmopower/examples/planck_cmb.yaml +++ b/cosmopower/examples/planck_cmb.yaml @@ -40,10 +40,6 @@ networks: - quantity: "derived" type: NN inputs: [ombh2, omch2, cosmomc_theta, ns, logA, tau] - log: True - modes: - label: l - range: [2, 2508] n_traits: n_hidden: [512,512,512,512] training: diff --git a/cosmopower/spectra.py b/cosmopower/spectra.py index 8947abd..ad06a88 100644 --- a/cosmopower/spectra.py +++ b/cosmopower/spectra.py @@ -59,7 +59,7 @@ def init_boltzmann_code(parser: YAMLParser) -> dict: res = {} code = parser.boltzmann_code - extra_args = parser.boltzmann_extra_args + extra_args = parser.boltzmann_extra_args or {} # Try to import the correct python module for spectrum generation try: @@ -256,7 +256,7 @@ def generate_spectra(args: list = None) -> None: f"files {first_file}--{last_file}.") state = init_boltzmann_code(parser) - extra_args = parser.boltzmann_extra_args + extra_args = parser.boltzmann_extra_args or {} accepted = 0 tbar = tqdm.tqdm(np.arange(first, last + 1)) @@ -265,7 +265,7 @@ def generate_spectra(args: list = None) -> None: for n in tbar: tbar.set_description(("" if MPI is None else f"[{rank}] ") - + f"{accepted/max(n,1):.1%} success rate") + + f"{accepted/max(n, 1):.1%} success rate") boltzmann_params = {k: training[k][n] for k in parser.boltzmann_inputs} diff --git a/cosmopower/spectra_camb.py b/cosmopower/spectra_camb.py index 7411cd0..d0865b2 100644 --- a/cosmopower/spectra_camb.py +++ b/cosmopower/spectra_camb.py @@ -48,13 +48,13 @@ def initialize(parser: YAMLParser, extra_args: dict = {}) -> dict: # Check that the maximum values for mode ranges matches # the requested outputs. - if parser.modes_label(quantity) == "l" and \ - extra_args["lmax"] < parser.modes(quantity).max(): - extra_args["lmax"] = parser.modes(quantity).max() + if parser.modes_label(quantity) == "l": + extra_args["lmax"] = max(extra_args["lmax"], + parser.modes(quantity).max()) - if parser.modes_label(quantity) == "k" and \ - extra_args["kmax"] < parser.modes(quantity).max(): - extra_args["kmax"] = parser.modes(quantity).max() + if parser.modes_label(quantity) == "k": + extra_args["kmax"] = max(extra_args["kmax"], + parser.modes(quantity).max()) if parser.modes_label(quantity) == "z": z1 = extra_args["redshifts"] @@ -223,12 +223,12 @@ def get_spectra(parser: YAMLParser, state: dict, args: dict = {}, except (camb.CAMBError, camb.CAMBFortranError): return False + state["derived"] = get_camb_derived(state["params"], state["results"], + list(state["derived"].keys())) + for quantity in quantities: qpath = quantity.split("/") - state["derived"] = get_camb_derived(state["params"], state["results"], - list(state["derived"].keys())) - if qpath[0] == "derived": continue elif qpath[0] == "Cl": @@ -275,14 +275,14 @@ def get_spectra(parser: YAMLParser, state: dict, args: dict = {}, state["params"], state["results"], parser.modes(qpath[0]) ) elif qpath[0] == "DA" or qpath[0] == "angular_diameter_distance": - # angular diameter distance - state[quantity] = get_camb_sigma8( + # angular diameter distance DA(z) + state[quantity] = get_camb_angular_diameter_distance( state["params"], state["results"], parser.modes(qpath[0]) ) elif qpath[0] in ["Omega_b", "Omega_cdm"]: # background evolution of densities var = {"Omega_b": "baryon", "Omega_cdm": "cdm"}.get(qpath[0]) - state[quantity] = get_camb_sigma8( + state[quantity] = get_camb_Omega( state["params"], state["results"], parser.modes(qpath[0]), var ) else: diff --git a/cosmopower/spectra_classy.py b/cosmopower/spectra_classy.py new file mode 100644 index 0000000..66fcc48 --- /dev/null +++ b/cosmopower/spectra_classy.py @@ -0,0 +1,184 @@ +from __future__ import annotations +from .parser import YAMLParser +import numpy as np +import classy +from typing import Sequence + + +def initialize(parser: YAMLParser, extra_args: dict = {}) -> dict: + state = {} + + for quantity in parser.quantities: + state[quantity + ".modes"] = parser.modes(quantity) + + if extra_args.get("auto", False): + extra_args.pop("auto") + extra_args = classy_args_interpret(parser, extra_args) + print(f"Auto-generated class settings: {extra_args}") + + state["code"] = "classy" + state["module"] = classy + state["params"] = extra_args + state["derived"] = {k: np.nan for k in parser.computed_parameters} + + return state + + +def classy_args_interpret(parser: YAMLParser, extra_args: dict = {}) -> dict: + extra_args["l_max_scalars"] = extra_args.get("l_max_scalars", 0) + extra_args["l_max_tensors"] = extra_args.get("l_max_tensors", 0) + + extra_args["lensing"] = "no" + cl_spec = set(extra_args.get("output", "").split(" ")) + cl_modes = set(extra_args.get("modes", "s").split(" ")) + want_cl = False + + for quantity in parser.quantities: + if quantity == "derived": + if "sigma8" in parser.computed_parameters: + cl_spec.add("mPk") + + qpath = quantity.split("/") + if qpath[0] == "Cl" or qpath[0] == "unlensed_Cl": + want_cl = True + if qpath[1] == "tt" or qpath[1] == "te": + cl_spec.add("tCl") + if qpath[1] == "te" or qpath[1] == "ee": + cl_spec.add("pCl") + if qpath[0] == "Cl" or qpath[1] == "pp": + extra_args["lensing"] = "yes" + cl_spec.add("lCl") + + if "s" in cl_modes: + extra_args["l_max_scalars"] = max(extra_args["l_max_scalars"], + parser.modes(quantity).max()) + if "t" in cl_modes: + extra_args["l_max_tensors"] = max(extra_args["l_max_tensors"], + parser.modes(quantity).max()) + elif qpath[0] == "Pk": + cl_spec.add("mPk") + zval = parser.parameter_value("z") + if type(zval) is float: + z_max_pk = zval + elif type(zval) is tuple: + z_max_pk = max(zval) + extra_args["z_max_pk"] = max(extra_args.get("z_max_pk", 0.0), + z_max_pk) + k_max_pk = parser.modes(quantity).max() + extra_args["P_k_max_1/Mpc"] = max(extra_args.get("P_k_max_1/Mpc", + 1.0), + k_max_pk) + + if qpath[1] == "nonlin": + extra_args["non_linear"] = "hmcode" + elif qpath[0] == "sigma8": + cl_spec.add("mPk") + z = parser.modes(quantity) + extra_args["z_max_pk"] = max(extra_args.get("z_max_pk", 0.0), + z.max()) + k_max_pk = 1e-1 + extra_args["P_k_max_1/Mpc"] = max(extra_args.get("P_k_max_1/Mpc", + 1.0), + k_max_pk) + + if "s" not in cl_modes or not want_cl: + extra_args.pop("l_max_scalars") + if "t" not in cl_modes or not want_cl: + extra_args.pop("l_max_tensors") + extra_args["output"] = " ".join(list(cl_spec)) + extra_args["modes"] = " ".join(list(cl_modes)) + + print(extra_args) + + return extra_args + + +def get_classy_derived(params: dict, cosmo: classy.Class, + parameters: Sequence[str]) -> dict: + res = {} + + for p in parameters: + if p == "rs_drag": + res[p] = cosmo.rs_drag() + elif p == "Omega_nu": + res[p] = cosmo.Omega_nu + elif p == "T_cmb": + res[p] = cosmo.T_cmb() + else: + res[p] = cosmo.get_current_derived_parameters([p])[p] + + return res + + +def get_spectra(parser: YAMLParser, state: dict, args: dict = {}, + quantities: list = [], extra_args: dict = {}) -> bool: + + classy = state["module"] + + if "z" in args: + z_pk = args.pop("z") + + try: + cosmo = classy.Class() + cosmo.set(state["params"] | args) + cosmo.compute() + except (classy.CosmoComputationError, classy.CosmoSevereError) as e: + print(str(e)) + cosmo.struct_cleanup() + cosmo.empty() + return False + + state["derived"] = get_classy_derived(state["params"], cosmo, + list(state["derived"].keys())) + + for quantity in quantities: + qpath = quantity.split("/") + + if qpath[0] == "derived": + continue + elif qpath[0] == "Cl": + spec = qpath[1] + + cls = cosmo.lensed_cl() + ell = cls["ell"] + Cl = cls[spec] + Dl = Cl * (ell * (ell + 1) / (2 * np.pi)) + + state[quantity] = Dl[state[quantity + ".modes"]] + elif qpath[0] == "unlensed_Cl": + spec = qpath[1] + + cls = cosmo.raw_cl() + ell = cls["ell"] + Cl = cls[spec] + Dl = Cl * (ell * (ell + 1) / (2 * np.pi)) + + state[quantity] = Dl[state[quantity + ".modes"]] + elif qpath[0] == "Pk": + k = state[quantity + ".modes"] + spec = qpath[1] + + if spec == "lin": + Pk = np.zeros_like(k) + for i in range(len(k)): + Pk[i] = cosmo.pk_lin(k[i], z_pk) + if spec == "nonlin": + Pk = np.zeros_like(k) + for i in range(len(k)): + Pk[i] = cosmo.pk(k[i], z_pk) + + state[quantity] = Pk + elif qpath[0] == "DA" or qpath[0] == "angular_diameter_distance": + z = state[quantity + ".modes"] + state[quantity] = cosmo.angular_distance(z) + elif qpath[0] == "Hubble": + z = state[quantity + ".modes"] + state[quantity] = cosmo.Hubble(z) + elif qpath[0] == "sigma8": + z = state[quantity + ".modes"] + state[quantity] = cosmo.sigma(8, z) + + cosmo.struct_cleanup() + cosmo.empty() + + return True diff --git a/cosmopower/train.py b/cosmopower/train.py index 6c4127a..0647f1d 100644 --- a/cosmopower/train.py +++ b/cosmopower/train.py @@ -25,7 +25,7 @@ def train_network_NN(parser: YAMLParser, quantity: str, device: str = "", """ filenames = glob.glob(os.path.join(parser.path, "spectra", quantity.replace("/", "_") - + ".[0-9].hdf5")) + + ".[0-9]*.hdf5")) if len(filenames) == 0: raise IOError(f"No files found to train quantity {quantity} with.") @@ -55,7 +55,7 @@ def train_network_NN(parser: YAMLParser, quantity: str, device: str = "", else: filenames = glob.glob(os.path.join(parser.path, "spectra", quantity.replace("/", "_") + - ".validation.[0-9].hdf5")) + ".validation.[0-9]*.hdf5")) validation = [Dataset(parser, quantity, os.path.basename(filename)) for filename in filenames] @@ -85,7 +85,7 @@ def train_network_PCAplusNN(parser: YAMLParser, quantity: str, """ filenames = glob.glob(os.path.join(parser.path, "spectra", quantity.replace("/", "_") - + ".[0-9].hdf5")) + + ".[0-9]*.hdf5")) saved_filename = os.path.join(parser.path, "networks", parser.network_filename(quantity)) @@ -111,7 +111,7 @@ def train_network_PCAplusNN(parser: YAMLParser, quantity: str, else: filenames = glob.glob(os.path.join(parser.path, "spectra", quantity.replace("/", "_") + - ".validation.[0-9].hdf5")) + ".validation.[0-9]*.hdf5")) validation = [Dataset(parser, quantity, os.path.basename(filename)) for filename in filenames] @@ -171,7 +171,7 @@ def show_training(args: Optional[list] = None) -> None: for n, quantity in enumerate(plot_quantities): filenames = glob.glob(os.path.join(parser.path, "spectra", quantity.replace("/", "_") - + ".[0-9].hdf5")) + + ".[0-9]*.hdf5")) if len(filenames) == 0: raise IOError(f"No files found for {quantity}.") @@ -273,7 +273,7 @@ def show_validation(args: Optional[list] = None) -> None: filenames = glob.glob(os.path.join(parser.path, "spectra", quantity.replace("/", "_") - + ".validation.[0-9].hdf5")) + + ".validation.[0-9]*.hdf5")) if len(filenames) == 0: raise IOError(f"No files found for {quantity}.") @@ -463,7 +463,7 @@ def show_validation(args: Optional[list] = None) -> None: ny = (len(parser.computed_parameters) // nx) filenames = glob.glob(os.path.join(parser.path, "spectra", - "derived.[0-9].hdf5")) + "derived.[0-9]*.hdf5")) if len(filenames) == 0: raise IOError("No files found for derived parameters.") diff --git a/cosmopower/validate.py b/cosmopower/validate.py index 567196e..923fbee 100644 --- a/cosmopower/validate.py +++ b/cosmopower/validate.py @@ -15,7 +15,7 @@ def find_files(parser: YAMLParser, quantity: str) -> np.ndarray: import glob fn = os.path.join(parser.path, "spectra", - quantity.replace("/", "_") + ".*.hdf5") + quantity.replace("/", "_") + ".[0-9]*.hdf5") return glob.glob(fn) diff --git a/cosmopower/wrappers/cobaya/cosmopower.py b/cosmopower/wrappers/cobaya/cosmopower.py index 645af7a..77c0beb 100644 --- a/cosmopower/wrappers/cobaya/cosmopower.py +++ b/cosmopower/wrappers/cobaya/cosmopower.py @@ -284,7 +284,6 @@ def get_Cl(self, ell_factor: bool = False, def get_requirements(self): - return self._params_required_by_networks def get_in_dict(self, dct: dict, path: str) -> Any: