def operations_dict_from_name(mod_name): constituents = model_building_utilities.get_constituent_names_from_name( mod_name) num_qubits = model_building_utilities.get_num_qubits(mod_name) initial_t_str = "" all_terms = [] for j in range(num_qubits - 1): initial_t_str += "T" for i in range(len(constituents)): t_str = initial_t_str single_term = constituents[i] all_tuples_this_term = [] n_minus_1_qubit_operators = single_term for k in range(num_qubits): if len(t_str) > 0: split_by_nth_qubit = n_minus_1_qubit_operators.split(t_str) this_tuple = (num_qubits - k, split_by_nth_qubit[1]) n_minus_1_qubit_operators = split_by_nth_qubit[0] t_str = t_str[:-1] else: this_tuple = (num_qubits - k, n_minus_1_qubit_operators) all_tuples_this_term.append(this_tuple) all_tuples_this_term = sorted(all_tuples_this_term) all_terms.append(all_tuples_this_term) operations = {"dim": num_qubits, "terms": all_terms} return operations
def increase_dimension_pauli_set(initial_model, new_dimension=None): individual_terms = model_building_utilities.get_constituent_names_from_name( initial_model) separate_terms = [] for model in individual_terms: components = model.split("_") for c in components: if c[0] == "d": current_dim = int(c.replace("d", "")) components.remove(c) if new_dimension is None: new_dimension = current_dim + 1 new_component = "d{}".format(new_dimension) components.append(new_component) new_mod = "_".join(components) separate_terms.append(new_mod) p_str = "P" * (new_dimension) full_model = p_str.join(separate_terms) # full_model = '+'.join(separate_terms) return full_model
def check_model_validity(self, model, **kwargs): # possibility that some models not valid; not needed by default but # checked for general case terms = model_building_utilities.get_constituent_names_from_name(model) if np.all(["FHhop" in a for a in terms]): return True elif np.all(["FHonsite" in a for a in terms]): # onsite present in all terms: discard # self.log_print( # ["Rejecting model", model, "b/c all onsite terms"] # ) return False else: hopping_sites = [] number_term_sites = [] chemical_sites = [] num_sites = model_building_utilities.get_num_qubits(model) for term in terms: constituents = term.split("_") constituents.remove("d{}".format(num_sites)) if "FHhop" in term: constituents.remove("FHhop") for c in constituents: if "h" in c: hopping_sites.extend(c.split("h")) elif "FHonsite" in term: constituents.remove("FHonsite") number_term_sites.extend(constituents) elif "FHchemical" in term: constituents.remove("FHchemical") chemical_sites.extend(constituents) # print("hopping_sites:", hopping_sites) # print('number term sites:', number_term_sites) hopping_sites = set(hopping_sites) number_term_sites = set(number_term_sites) overlap = number_term_sites.intersection(hopping_sites) if number_term_sites.issubset(hopping_sites): return True else: # no overlap between hopping sites and number term sites # so number term will be constant self.log_print( [ "Rejecting model", model, "bc number terms present" "which aren't present in kinetic term", ] ) return False
def uniform_prior( model_name, param_minimum=0, param_maximum=1, default_sigma=None, random_mean=False, # if set to true, chooses a random mean between given uniform min/max prior_specific_terms={}, log_file="qmd.log", log_identifier=None, **kwargs): individual_terms = model_building_utilities.get_constituent_names_from_name( model_name) num_terms = len(individual_terms) available_specific_terms = list(prior_specific_terms.keys()) u = [[param_minimum, param_maximum]] * num_terms u = np.array(u) dist = qinfer.UniformDistribution(u) return dist
def add_terms_greedy( self, models_to_build_on, available_terms, model_names_ids, # model_points, **kwargs): # models_to_build_on = [ # kwargs['model_names_ids'][mod_id] for mod_id in models_to_build_on # ] self.log_print([ "[Greedy add terms] models to build on {}".format( models_to_build_on) ]) new_models = [] models_by_parent = {} for mod_id in models_to_build_on: mod_name = model_names_ids[mod_id] models_by_parent[mod_id] = 0 mod_name = self.match_dimension(mod_name, self.topology.num_sites()) present_terms = model_building_utilities.get_constituent_names_from_name( mod_name) terms_to_add = list(set(available_terms) - set(present_terms)) if len(terms_to_add) == 0: # this dimension exhausted # return branch champs for this generation so far # such that final branch computes this generation champion self.spawn_stage.append("finish_generation") new_models = [ self.generation_champs[self.generation_DAG][k] for k in list(self.generation_champs[self.generation_DAG].keys()) ] new_models = flatten(new_models) # new_models = [new_models[0]] # hack to force non-crash for # single generation self.log_print([ "No remaining available terms. Completing generation", self.generation_DAG, "\nSub generation champion models:", new_models, ]) if self.generation_DAG == self.max_num_generations: # this was the final generation to learn. # instead of building new generation, skip straight to # Complete stage self.log_print(["Completing exploration strategy"]) self.spawn_stage.append("Complete") else: for term in terms_to_add: new_mod = self.combine_terms(terms=[mod_name, term]) if self.determine_whether_to_include_model(mod_id) == True: new_models.append(new_mod) self.models_accepted[self.generation_DAG].append( new_mod) models_by_parent[mod_id] += 1 else: self.models_rejected[self.generation_DAG].append( new_mod) self.log_print( ["# models added by parent: {}".format(models_by_parent)]) return new_models
def generate_models(self, model_list, **kwargs): if self.spawn_stage[-1] == "Start": new_models = self.available_mods_by_generation[self.generation_DAG] # self.log_print(["Spawning initial models:", new_models]) self.spawn_stage.append(None) else: # model_points = kwargs['branch_model_points'] ranked_model_list = self.model_group_fitness_calculation( model_points=kwargs["branch_model_points"], generation=self.generation_DAG, subgeneration=self.sub_generation_idx, ) if self.num_top_models_to_build_on == "all": models_to_build_on = ranked_model_list else: models_to_build_on = ranked_model_list[:self. num_top_models_to_build_on] self.sub_generation_idx += 1 self.generation_champs[self.generation_DAG][ self.sub_generation_idx] = [ kwargs["model_names_ids"][models_to_build_on[0]] ] self.counter += 1 new_models = [] if self.spawn_stage[-1] == "finish_generation": # increase generation idx; add site; get newly available terms; # add greedily as above self.new_generation() if self.spawn_stage[-1] is None: # new models given by models_to_build_on plus terms in # available_terms (greedy) new_models = self.add_terms_greedy( models_to_build_on=models_to_build_on, available_terms=self.available_mods_by_generation[ self.generation_DAG], model_names_ids=kwargs["model_names_ids"], # model_points=model_points ) new_models = [ model_building_utilities.alph(mod) for mod in new_models # Final check whether this model is allowed if self.check_model_validity(mod) ] # store branch idx for new models registered_models = list(self.model_branches.keys()) for model in new_models: if model not in registered_models: latex_model_name = self.latex_name(model) branch_id = self.generation_DAG + len( model_building_utilities.get_constituent_names_from_name( model)) self.model_branches[latex_model_name] = branch_id return new_models
def terms_names(self): """ List of constituent operators names. """ return get_constituent_names_from_name(self.name)
def plot_learned_models_dynamics( qmd, model_ids=None, include_expec_vals=True, include_bayes_factors=True, include_times_learned=True, include_param_estimates=False, save_to_file=None, ): model_ids = list(sorted(set(model_ids))) # only uniques values true_expec_vals = pickle.load( open(qmd.qmla_controls.system_measurements_file, "rb") ) times_to_plot = list(sorted(true_expec_vals.keys())) # TODO this is overwritten within for loop below so that large # Hamiltonians don't have to work out each time step true_exp = [true_expec_vals[t] for t in times_to_plot] qmd.log_print( [ "[Dynamics plot] plot probe file:", qmd.qmla_controls.probes_plot_file, "\n true expectation value path:", qmd.qmla_controls.system_measurements_file, ] ) plot_probes = pickle.load(open(qmd.qmla_controls.probes_plot_file, "rb")) num_models_to_plot = len(model_ids) all_bayes_factors = qmd.all_bayes_factors max_time = max(times_to_plot) individual_terms_already_in_legend = [] # ncols = int(np.ceil(np.sqrt(num_models_to_plot))) # nrows = 3*int(np.ceil(num_models_to_plot/ncols)) + 1 # 1 extra row for # "master" ncols = ( include_expec_vals + include_bayes_factors + include_times_learned + include_param_estimates ) # ncols = 4 nrows = num_models_to_plot fig = plt.figure( figsize=(18, 10), # constrained_layout=True, tight_layout=True, ) gs = GridSpec( nrows, ncols, # figure=fig # not available on matplotlib 2.1.1 (on BC) ) row = 0 col = 0 for mod_id in model_ids: qmd.log_print(["Plotting dynamics for model {}".format(mod_id)]) reduced = qmd.get_model_storage_instance_by_id(mod_id) reduced.compute_expectation_values(times=qmd.times_to_plot) # exploration_rule = reduced.exploration_strategy_of_true_model desc = str("ID:{}\n".format(mod_id) + reduced.model_name_latex) times_to_plot = list(sorted(true_expec_vals.keys())) plot_colour = "blue" name_colour = "black" dynamics_label = str(mod_id) try: true_model_id = qmd.true_model_id except BaseException: true_model_id = -1 if mod_id == qmd.champion_model_id and mod_id == true_model_id: plot_colour = "green" name_colour = "green" dynamics_label += " [true + champ]" desc += str("\n[True + Champ]") elif mod_id == qmd.champion_model_id: plot_colour = "orange" name_colour = "orange" dynamics_label += " [champ]" desc += str("\n[Champ]") elif mod_id == true_model_id: plot_colour = "green" name_colour = "green" dynamics_label += " [true]" desc += str("\n[True]") ############ --------------- ############ ############ Plot dynamics in left most column ############ ############ --------------- ############ if include_expec_vals is True: ham = reduced.learned_hamiltonian dim = np.log2(np.shape(ham)[0]) probe = plot_probes[reduced.probe_num_qubits] qmd.log_print( [ "[plot_learned_models_dynamics]", "\n\tModel ", reduced.model_name_latex, "\n\tnum qubits:", dim, "\n\tprobe:", probe, ] ) # expec_vals = {} if dim > 4: times_to_plot = times_to_plot[0::5] times_to_plot = sorted(list(true_expec_vals.keys())) true_exp = [true_expec_vals[t] for t in times_to_plot] # choose an axis to plot on ax = fig.add_subplot(gs[row, col]) # first plot true dynamics ax.plot(times_to_plot, true_exp, c="r") # now plot learned dynamics expec_vals = reduced.expectation_values sim_times = sorted(list(expec_vals.keys())) sim_exp = [expec_vals[t] for t in sim_times] # print( # "[plotDynamics]", # "\nsim exp:", sim_exp, # "\nsim_times:", sim_times # ) ax.plot( sim_times, sim_exp, marker="o", markevery=10, c=plot_colour, # label = dynamics_label label=desc, ) # qmd.log_print([ # "[Dynamics plot]", # "sim_exp:", sim_exp[0:20], # "true exp:", true_exp[0:20] # ]) # ax.legend() ax.set_ylim(-0.05, 1.05) if row == 0: ax.set_title("Expectation Values") if ncols == 1: ax.legend() col += 1 if col == ncols: col = 0 row += 1 ############ --------------- ############ ############ Plot Bayes factors ############ ############ --------------- ############ if include_bayes_factors == True: bayes_factors_this_mod = [] bf_opponents = [] for b in model_ids: if b != mod_id: if b in list(all_bayes_factors[mod_id].keys()): # bf_opponents.append( # qmd.get_model_storage_instance_by_id(b).model_name_latex # ) bayes_factors_this_mod.append( np.log10(all_bayes_factors[mod_id][b][-1]) ) bf_opponents.append(str(b)) ax = fig.add_subplot(gs[row, col]) ax.bar(bf_opponents, bayes_factors_this_mod, color=plot_colour) ax.axhline(0, color="black") if row == 0: ax.set_title("Bayes Factors [$log_{10}$]") col += 1 if col == ncols: col = 0 row += 1 ############ --------------- ############ ############ Plot times learned over ############ ############ --------------- ############ if include_times_learned == True: ax = fig.add_subplot(gs[row, col]) if row == 0: ax.set_title("Times learned") ax.yaxis.set_label_position("right") times_learned_over = sorted( qmla.utilities.flatten(reduced.times_learned_over) ) qmd.log_print(["[single instance plot] Times for bin:", times_learned_over]) n, bins, patches = ax.hist( times_learned_over, # histtype='step', color=plot_colour, # fill=False, label=desc, ) ax.legend() # ax.semilogy() for bin_value in bins: ax.axvline(bin_value, linestyle="--", alpha=0.3) plot_time_max = max(times_to_plot) max_time = max(times_learned_over) if max_time > plot_time_max: ax.axvline( plot_time_max, color="red", linestyle="--", label="Dynamics plot cutoff", ) ax.legend() ax.set_xlim(0, max_time) col += 1 if col == ncols: col = 0 row += 1 ############ --------------- ############ ############ Plot parameters estimates ############ ############ --------------- ############ if include_param_estimates == True: ax = fig.add_subplot(gs[row, col]) name = reduced.model_name terms = model_building_utilities.get_constituent_names_from_name(name) num_terms = len(terms) term_positions = {} param_estimate_by_term = {} std_devs = {} for t in range(num_terms): term_positions[terms[t]] = t term = terms[t] param_position = term_positions[term] param_estimates = reduced.track_param_means[:, param_position] # std_dev = mod.cov_matrix[param_position,param_position] # std_dev = reduced.track_covariance_matrices[ # :,param_position,param_position # ] std_dev = reduced.track_param_uncertainties[:, param_position] param_estimate_by_term[term] = param_estimates std_devs[term] = std_dev cm_subsection = np.linspace(0, 0.8, num_terms) colours = [cm.magma(x) for x in cm_subsection] # TODO use color map as list num_epochs = reduced.num_experiments i = 0 # for term in list(param_estimate_by_term.keys()): for term in terms: colour = colours[i % len(colours)] i += 1 try: y_true = qmd.true_param_dict[term] true_term_latex = qmd.exploration_class.latex_name(name=term) ax.axhline( y_true, ls="--", label=str(true_term_latex + " True"), color=colour, ) except BaseException: pass y = np.array(param_estimate_by_term[term]) s = np.array(std_devs[term]) x = range(1, 1 + len(param_estimate_by_term[term])) latex_term = qmd.exploration_class.latex_name(name=term) if latex_term not in individual_terms_already_in_legend: individual_terms_already_in_legend.append(latex_term) plot_label = str(latex_term) else: plot_label = "" # print("[pQMD] latex_term:", latex_term) ax.plot( x, y, # s=max(1,50/num_epochs), label=plot_label, color=colour, ) # ax.set_yscale('symlog') # print("[pQMD] scatter done" ) # ax.fill_between( # x, # y+s, # y-s, # alpha=0.2, # facecolor=colour # ) ax.legend() if row == 0: ax.set_title("Parameter Estimates") col += 1 if col == ncols: col = 0 row += 1 if save_to_file is not None: plt.savefig(save_to_file, bbox_inches="tight")
def plot_parameter_estimates(qmd, model_id, save_to_file=None): from matplotlib import cm mod = qmd.get_model_storage_instance_by_id(model_id) name = mod.model_name if name not in list(qmd.model_name_id_map.values()): print( "True model ", name, "not in studied models", list(qmd.model_name_id_map.values()), ) return False terms = model_building_utilities.get_constituent_names_from_name(name) num_terms = len(terms) term_positions = {} param_estimate_by_term = {} std_devs = {} for t in range(num_terms): term_positions[terms[t]] = t term = terms[t] param_position = term_positions[term] param_estimates = mod.track_param_means[:, param_position] # std_dev = mod.cov_matrix[param_position,param_position] std_dev = mod.track_covariance_matrices[:, param_position, param_position] param_estimate_by_term[term] = param_estimates std_devs[term] = std_dev cm_subsection = np.linspace(0, 0.8, num_terms) colours = [cm.magma(x) for x in cm_subsection] # TODO use color map as list num_epochs = mod.num_experiments ncols = int(np.ceil(np.sqrt(num_terms))) nrows = int(np.ceil(num_terms / ncols)) fig, axes = plt.subplots(figsize=(10, 7), nrows=nrows, ncols=ncols, squeeze=False) row = 0 col = 0 axes_so_far = 0 i = 0 # for term in list(param_estimate_by_term.keys()): for term in terms: ax = axes[row, col] colour = colours[i % len(colours)] i += 1 try: y_true = qmd.true_param_dict[term] true_term_latex = qmd.exploration_class.latex_name(name=term) true_term_latex = true_term_latex[:-1] + "_{0}" + "$" ax.axhline(y_true, label=str(true_term_latex), color="red", linestyle="--") except BaseException: pass y = np.array(param_estimate_by_term[term]) s = np.array(std_devs[term]) x = range(1, 1 + len(param_estimate_by_term[term])) latex_term = mod.exploration_class.latex_name(term) latex_term = latex_term[:-1] + r"^{\prime}" + "$" # print("[pQMD] latex_term:", latex_term) ax.scatter(x, y, s=max(1, 50 / num_epochs), label=str(latex_term), color=colour) # ax.set_yscale('symlog') # print("[pQMD] scatter done" ) ax.fill_between( x, y + s, y - s, alpha=0.2, facecolor="green", # label='$\sigma$' ) # print("[pQMD] fill between done") ax.legend(loc=1, fontsize=20) axes_so_far += 1 col += 1 if col == ncols: col = 0 row += 1 # ax.set_title(str(latex_term)) # print("[pQMD] title set") # ax = plt.subplot(111) plt.xlabel("Epoch", fontsize=20) plt.ylabel("Parameter Estimate", fontsize=15) if save_to_file is not None: print( "[plot_parameter_estimates] saving to file", save_to_file, "type:", type(save_to_file), ) plt.savefig(save_to_file, bbox_inches="tight")
def gaussian_prior( model_name, param_minimum=0, param_maximum=1, default_sigma=None, random_mean=False, # if set to true, chooses a random mean between given uniform min/max prior_specific_terms={}, log_file="qmd.log", log_identifier=None, **kwargs): """ Genearates a QInfer Gaussian distribution . Given a model_name, deteremines the number of terms in the model, N. Generates a multivariate distribution with N dimensions. This is then used as the initial prior, which QHL uses to learn the model parameters. By default, each parameter's mean is the average of param_min and param_max, with sigma = mean/4. This can be changed by specifying prior_specific_terms: individual parameter's means/sigmas can be given. :param str model_name: Unique string representing a model. :param float param_minimum: Lower bound for distribution. :param float param_maximum: Upper bound for distribution. :param float default_sigma: Width of distribution desired. If None, defaults to 0.25 * (param_max - param_min). :param dict prior_specific_terms: Individual parameter mean and sigma to enforce in the distribution. :param str log_file: Path of the log file for logging errors. :param str log_identifier: Unique identifying sting for logging. :return QInfer.Distribution dist: distribution to be used as prior for parameter learning of the named model. """ log_print( [ "Getting prior for model:", model_name, "Specific terms:", prior_specific_terms, ], log_file, log_identifier, ) individual_terms = model_building_utilities.get_constituent_names_from_name( model_name) num_terms = len(individual_terms) available_specific_terms = list(prior_specific_terms.keys()) means = [] sigmas = [] default_mean = np.mean([param_minimum, param_maximum]) # TODO reconsider how default sigma is generated # default_sigma = default_mean/2 # TODO is this safe? if default_sigma is None: default_sigma = (param_maximum - param_minimum) / 4 for term in individual_terms: if term in available_specific_terms: means.append(prior_specific_terms[term][0]) sigmas.append(prior_specific_terms[term][1]) else: if random_mean: rand_mean = random.uniform(param_minimum, param_maximum) means.append(rand_mean) else: means.append(default_mean) sigmas.append(default_sigma) means = np.array(means) sigmas = np.array(sigmas) cov_mtx = np.diag(sigmas**2) dist = qinfer.MultivariateNormalDistribution(means, cov_mtx) return dist