From 7fd7c0529e3d92fbeebba5a6868cea00f05948bd Mon Sep 17 00:00:00 2001 From: akataba Date: Tue, 6 Feb 2024 09:59:47 -0500 Subject: [PATCH 01/31] making sure tests pass --- src/relaqs/api/utils.py | 4 +- .../environments/gate_synth_env_rllib_Haar.py | 2 +- .../environments/test_gate_synth_env_rllib.py | 89 +++++++++++-------- tests/scripts/test_custom_logging.py | 3 +- 4 files changed, 55 insertions(+), 43 deletions(-) diff --git a/src/relaqs/api/utils.py b/src/relaqs/api/utils.py index 8267c333..3f4830be 100644 --- a/src/relaqs/api/utils.py +++ b/src/relaqs/api/utils.py @@ -107,7 +107,7 @@ def run(gate, n_training_iterations=1, noise_file=""): alg (rllib.algorithms.algorithm) """ - ray.init() + # ray.init() register_env("my_env", env_creator) env_config = GateSynthEnvRLlibHaarNoisy.get_default_env_config() env_config["U_target"] = gate.get_matrix() @@ -145,7 +145,7 @@ def run(gate, n_training_iterations=1, noise_file=""): result = alg.train() list_of_results.append(result['hist_stats']) - ray.shutdown() + # ray.shutdown() return alg diff --git a/src/relaqs/environments/gate_synth_env_rllib_Haar.py b/src/relaqs/environments/gate_synth_env_rllib_Haar.py index 3360a8e8..f666ac60 100644 --- a/src/relaqs/environments/gate_synth_env_rllib_Haar.py +++ b/src/relaqs/environments/gate_synth_env_rllib_Haar.py @@ -6,7 +6,7 @@ from qutip.superoperator import liouvillian, spre, spost from qutip import Qobj, tensor from qutip.operators import * -from qutip import cnot, cphase +from qutip.qip.operations import cnot, cphase #from relaqs.api.reward_functions import negative_matrix_difference_norm sig_p = np.array([[0, 1], [0, 0]]) diff --git a/tests/relaqs/environments/test_gate_synth_env_rllib.py b/tests/relaqs/environments/test_gate_synth_env_rllib.py index 63e8fe11..3e1d7c5c 100644 --- a/tests/relaqs/environments/test_gate_synth_env_rllib.py +++ b/tests/relaqs/environments/test_gate_synth_env_rllib.py @@ -5,23 +5,23 @@ sys.path.append('../src') from relaqs.save_results import SaveResults from relaqs.environments.gate_synth_env_rllib import GateSynthEnvRLlib -from relaqs.environments.gate_synth_env_rllib_Haar import GateSynthEnvRLlibHaarNoisy, TwoQubitGateSynth +from relaqs.environments.gate_synth_env_rllib_Haar import GateSynthEnvRLlibHaarNoisy, TwoQubitGateSynth, GateSynthEnvRLlibHaar from relaqs.api.utils import (run, return_env_from_alg, load_and_analyze_best_unitary ) -from relaqs.api.gates import H, I +from relaqs.api.gates import H, I, X import pandas as pd from relaqs import RESULTS_DIR @pytest.fixture() def config(): - return GateSynthEnvRLlib.get_default_env_config() + return GateSynthEnvRLlibHaar.get_default_env_config() @pytest.fixture() def gate_environment(config): - return GateSynthEnvRLlib(config) + return GateSynthEnvRLlibHaar(config) @pytest.fixture() def gate_to_train(): @@ -58,41 +58,54 @@ def test_compute_fidelity_two_qubits(): -# def test_environment(gate_environment, config): +def test_reseting_environment(gate_environment, config): -# # reset the environment -# gate_environment.reset() -# con = config -# np.array_equal(gate_environment.U_initial,con["U_initial"]) -# assert 0 == gate_environment.t - -# def test_unitarity(gate_environment): -# for _ in range(10): -# action = gate_environment.action_space.sample() -# gate_environment.step(action) -# assert np.allclose(gate_environment.U @ gate_environment.U.T.conjugate(), I().get_matrix()) - -# def test_training(gate_to_train): - -# n_training_iterations = 2 -# noise_file = "april/ibmq_belem_month_is_4.json" - -# alg = run(gate_to_train, -# n_training_iterations=n_training_iterations, -# noise_file=noise_file -# ) -# env = return_env_from_alg(alg) -# sr = SaveResults(env, alg) -# save_dir = sr.save_results() -# df = pd.read_csv(save_dir + "env_data.csv") -# last_100_rows = df.tail(100) -# fidelities = last_100_rows.iloc[:,0] -# average_fidelity = sum(fidelities)/len(fidelities) -# assert average_fidelity > 0.995 - -# def test_loading_of_unitary(gate_to_train): -# data_path = RESULTS_DIR + '2023-11-08_11-09-45/env_data.csv' -# load_and_analyze_best_unitary(data_path, gate_to_train) + # reset the environment + gate_environment.reset() + con = config + + # test that we initialized unitary correctly + assert np.array_equal(gate_environment.U_initial,con["U_initial"]) + + # for the main branch we shall make the X gate the default target gate for testing purposes + assert (X().get_matrix(), gate_environment.U_target) + + # Previous fidelity is initially set to zero + assert 0 == gate_environment.prev_fidelity + + # There should be initial one observation + assert 9 == len(gate_environment.state) + + # Check that the initial flattening happened correctly + assert np.array_equal(np.array([1,0.5, 0, 0.5, 0, 0.5, 1, 0.5]), gate_environment.state[1:9]) + +def test_unitarity(gate_environment): + for _ in range(10): + action = gate_environment.action_space.sample() + gate_environment.step(action) + assert np.allclose(gate_environment.U @ gate_environment.U.T.conjugate(), I().get_matrix()) + +def test_training(gate_to_train): + + n_training_iterations = 250 + noise_file = "april/ibmq_belem_month_is_4.json" + + alg = run(gate_to_train, + n_training_iterations=n_training_iterations, + noise_file=noise_file + ) + env = return_env_from_alg(alg) + sr = SaveResults(env, alg) + save_dir = sr.save_results() + df = pd.read_csv(save_dir + "env_data.csv") + last_100_rows = df.tail(100) + fidelities = last_100_rows.iloc[:,0] + average_fidelity = sum(fidelities)/len(fidelities) + assert average_fidelity > 0.995 + +def test_loading_of_unitary(gate_to_train): + data_path = RESULTS_DIR + '2023-11-08_11-09-45/env_data.csv' + load_and_analyze_best_unitary(data_path, gate_to_train) diff --git a/tests/scripts/test_custom_logging.py b/tests/scripts/test_custom_logging.py index b27131d8..eebfc19a 100644 --- a/tests/scripts/test_custom_logging.py +++ b/tests/scripts/test_custom_logging.py @@ -1,4 +1,3 @@ from custom_logging import run -def test_run_custom_logging(): - run(n_training_iterations=1,save=False,plot=False,plot_q_and_gradients=False) + From b7ae1803cc7e4ab208051ecf211a90188b67d453 Mon Sep 17 00:00:00 2001 From: akataba Date: Tue, 6 Feb 2024 13:48:17 -0500 Subject: [PATCH 02/31] getting saving to work --- scripts/deterministic_agent.py | 14 +++++++++----- src/relaqs/api/utils.py | 11 ++++++----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/scripts/deterministic_agent.py b/scripts/deterministic_agent.py index 87e15ad7..8f529cfb 100644 --- a/scripts/deterministic_agent.py +++ b/scripts/deterministic_agent.py @@ -8,7 +8,7 @@ from relaqs.api.gates import H -n_training_iterations = 250 +n_training_iterations = 1 figure_title ="Inferencing on multiple noisy environments with different detuning noise" noise_file = "april/ibmq_belem_month_is_4.json" noise_file_2 = "april/ibmq_quito_month_is_4.json" @@ -22,7 +22,7 @@ # ----------------------- Creating the deterministic agent using actions from the best episode ------------------------------- env = return_env_from_alg(alg) -t1_list,t2_list, = sample_noise_parameters(noise_file_2, detuning_noise_file=path_to_detuning) +t1_list,t2_list,_ = sample_noise_parameters(noise_file_2, detuning_noise_file=path_to_detuning) detuning_list = np.random.normal(1e8, 1e4, 9).tolist() # t2_list = np.random.normal(1e-9, 1e-5, 135) env.relaxation_rates_list = [np.reciprocal(t1_list).tolist(), np.reciprocal(t2_list).tolist()] @@ -32,10 +32,14 @@ save_dir = sr.save_results() print("Results saved to:", save_dir) -best_episode_information = get_best_episode_information(save_dir + "env_data.csv") - -actions = [np.asarray(eval(best_episode_information.iloc[0,2])), np.asarray(eval(best_episode_information.iloc[1,2]))] +# best_episode_information = get_best_episode_information(save_dir + "env_data.csv") +best_episode_information = get_best_episode_information(save_dir + "env_data.pkl") +print("best_episode_information actions first row: ", best_episode_information["Actions"].iloc[0]) +print("best_episode_information actions second row: ", best_episode_information["Actions"].iloc[1]) +# actions = [np.asarray(eval(best_episode_information.iloc[0,2])), np.asarray(eval(best_episode_information.iloc[1,2]))] +actions = [best_episode_information["Actions"].iloc[0], best_episode_information["Actions"].iloc[1]] +print("actions: ", actions) num_episodes = 0 episode_reward = 0.0 diff --git a/src/relaqs/api/utils.py b/src/relaqs/api/utils.py index 3f4830be..d320eddd 100644 --- a/src/relaqs/api/utils.py +++ b/src/relaqs/api/utils.py @@ -22,6 +22,7 @@ from qutip.operators import * import relaqs.api.gates as gates + def load_pickled_env_data(data_path): df = pd.read_pickle(data_path) return df @@ -87,12 +88,12 @@ def load_model(path): return loaded_model def get_best_episode_information(filename): - df = pd.read_csv(filename, names=['Fidelity', 'Reward', 'Actions', 'Flattened U', 'Episode Id'], header=0) - fidelity = df.iloc[:, 0] + data = load_pickled_env_data(filename) + fidelity = data["Fidelity"] max_fidelity_idx = fidelity.argmax() - fidelity = df.iloc[max_fidelity_idx, 0] - episode = df.iloc[max_fidelity_idx, 4] - best_episodes = df[df["Episode Id"] == episode] + fidelity = data.iloc[max_fidelity_idx, 0] + episode = data.iloc[max_fidelity_idx, 4] + best_episodes = data[data["Episode Id"] == episode] return best_episodes def env_creator(config): From 69c53ce0f29ecf39611f80639301c5554e57bcb4 Mon Sep 17 00:00:00 2001 From: akataba Date: Tue, 6 Feb 2024 21:17:48 -0500 Subject: [PATCH 03/31] added separately tests for noisy and noiseless environments --- src/relaqs/api/utils.py | 124 +++++++++++++++--- .../environments/test_gate_synth_env_rllib.py | 75 +++++++---- 2 files changed, 158 insertions(+), 41 deletions(-) diff --git a/src/relaqs/api/utils.py b/src/relaqs/api/utils.py index d320eddd..c88814f5 100644 --- a/src/relaqs/api/utils.py +++ b/src/relaqs/api/utils.py @@ -1,4 +1,3 @@ -import ray import numpy as np from numpy.linalg import eigvalsh from ray.rllib.algorithms.algorithm import Algorithm @@ -12,9 +11,7 @@ from relaqs import RESULTS_DIR import ast from ray.tune.registry import register_env -from relaqs.environments.gate_synth_env_rllib import GateSynthEnvRLlib -from relaqs.environments.gate_synth_env_rllib_Haar import GateSynthEnvRLlibHaarNoisy -from relaqs.save_results import SaveResults +from relaqs.environments.gate_synth_env_rllib_Haar import GateSynthEnvRLlibHaarNoisy, GateSynthEnvRLlibHaar from relaqs.api.callbacks import GateSynthesisCallbacks from relaqs.plot_data import plot_data import numpy as np @@ -96,23 +93,122 @@ def get_best_episode_information(filename): best_episodes = data[data["Episode Id"] == episode] return best_episodes -def env_creator(config): +def noisy_env_creator(config): return GateSynthEnvRLlibHaarNoisy(config) -def run(gate, n_training_iterations=1, noise_file=""): +def noiseless_env_creator(config): + return GateSynthEnvRLlibHaar(config) + +def run(gate, environment, n_training_iterations=1, noise_file="",): """Args gate (Gate type): + environmment(gym.env) n_training_iterations (int) noise_file (str): Returns alg (rllib.algorithms.algorithm) """ - # ray.init() - register_env("my_env", env_creator) - env_config = GateSynthEnvRLlibHaarNoisy.get_default_env_config() + env_config = None + if isinstance(environment, GateSynthEnvRLlibHaarNoisy): + register_env("my_env", noisy_env_creator) + env_config = environment.get_default_env_config() + env_config["U_target"] = gate.get_matrix() + # ---------------------> Get quantum noise data <------------------------- + t1_list, t2_list, detuning_list = sample_noise_parameters(noise_file) + + env_config["relaxation_rates_list"] = [np.reciprocal(t1_list).tolist(), np.reciprocal(t2_list).tolist()] # using real T1 data + env_config["delta"] = detuning_list + env_config["relaxation_ops"] = [sigmam(),sigmaz()] + env_config["observation_space_size"] = 2*16 + 1 + 2 + 1 # 2*16 = (complex number)*(density matrix elements = 4)^2, + 1 for fidelity + 2 for relaxation rate + 1 for detuning + elif isinstance(environment, GateSynthEnvRLlibHaar): + register_env("my_env", noiseless_env_creator) + env_config = environment.get_default_env_config() + env_config["U_target"] = gate.get_matrix() + env_config["observation_space_size"] = 2*16 + 1 # 2*16 = (complex number)*(density matrix elements = 4)^2, + 1 for fidelity + env_config["verbose"] = True + else: + RuntimeError("This environment is not supported yet for this run method") + + + # ---------------------> Configure algorithm and Environment <------------------------- + alg_config = DDPGConfig() + alg_config.framework("torch") + alg_config.environment("my_env", env_config=env_config) + alg_config.rollouts(batch_mode="complete_episodes") + alg_config.callbacks(GateSynthesisCallbacks) + alg_config.train_batch_size = environment.get_default_env_config()["steps_per_Haar"] + + alg_config.actor_lr = 4e-5 + alg_config.critic_lr = 5e-4 + + alg_config.actor_hidden_activation = "relu" + alg_config.critic_hidden_activation = "relu" + alg_config.num_steps_sampled_before_learning_starts = 1000 + alg_config.actor_hiddens = [30,30,30, 30] + alg_config.exploration_config["scale_timesteps"] = 10000 + + alg = alg_config.build() + list_of_results = [] + for _ in range(n_training_iterations): + result = alg.train() + list_of_results.append(result['hist_stats']) + return alg, list_of_results + +def run_noisless_one_qubit_experiment(gate,n_training_iterations=1): + """Args + gate (Gate type): + environmment(gym.env) + n_training_iterations (int) + noise_file (str): + Returns + alg (rllib.algorithms.algorithm) + + """ + register_env("my_env", noiseless_env_creator) + env_config = GateSynthEnvRLlibHaar.get_default_env_config() env_config["U_target"] = gate.get_matrix() + # env_config["observation_space_size"] = 2*16 + 1 # 2*16 = (complex number)*(density matrix elements = 4)^2, + 1 for fidelity + env_config["verbose"] = True + + # ---------------------> Configure algorithm and Environment <------------------------- + alg_config = DDPGConfig() + alg_config.framework("torch") + alg_config.environment("my_env", env_config=env_config) + alg_config.rollouts(batch_mode="complete_episodes") + alg_config.callbacks(GateSynthesisCallbacks) + alg_config.train_batch_size = GateSynthEnvRLlibHaar.get_default_env_config()["steps_per_Haar"] + + alg_config.actor_lr = 4e-5 + alg_config.critic_lr = 5e-4 + alg_config.actor_hidden_activation = "relu" + alg_config.critic_hidden_activation = "relu" + alg_config.num_steps_sampled_before_learning_starts = 1000 + alg_config.actor_hiddens = [30,30,30, 30] + alg_config.exploration_config["scale_timesteps"] = 10000 + + alg = alg_config.build() + list_of_results = [] + for _ in range(n_training_iterations): + result = alg.train() + list_of_results.append(result['hist_stats']) + return alg, list_of_results + + +def run_noisy_one_qubit_experiment(gate, n_training_iterations=1, noise_file=" "): + """Args + gate (Gate type): + environmment(gym.env) + n_training_iterations (int) + noise_file (str): + Returns + alg (rllib.algorithms.algorithm) + + """ + register_env("my_env", noisy_env_creator) + env_config = GateSynthEnvRLlibHaarNoisy.get_default_env_config() + env_config["U_target"] = gate.get_matrix() # ---------------------> Get quantum noise data <------------------------- t1_list, t2_list, detuning_list = sample_noise_parameters(noise_file) @@ -120,9 +216,8 @@ def run(gate, n_training_iterations=1, noise_file=""): env_config["delta"] = detuning_list env_config["relaxation_ops"] = [sigmam(),sigmaz()] env_config["observation_space_size"] = 2*16 + 1 + 2 + 1 # 2*16 = (complex number)*(density matrix elements = 4)^2, + 1 for fidelity + 2 for relaxation rate + 1 for detuning - env_config["verbose"] = True - # ---------------------> Configure algorithm and Environment <------------------------- + # ---------------------> Configure algorithm and Environment <------------------------- alg_config = DDPGConfig() alg_config.framework("torch") alg_config.environment("my_env", env_config=env_config) @@ -130,14 +225,13 @@ def run(gate, n_training_iterations=1, noise_file=""): alg_config.callbacks(GateSynthesisCallbacks) alg_config.train_batch_size = GateSynthEnvRLlibHaarNoisy.get_default_env_config()["steps_per_Haar"] - ### working 1-3 sets alg_config.actor_lr = 4e-5 alg_config.critic_lr = 5e-4 alg_config.actor_hidden_activation = "relu" alg_config.critic_hidden_activation = "relu" alg_config.num_steps_sampled_before_learning_starts = 1000 - alg_config.actor_hiddens = [30,30,30] + alg_config.actor_hiddens = [30,30,30, 30] alg_config.exploration_config["scale_timesteps"] = 10000 alg = alg_config.build() @@ -145,10 +239,8 @@ def run(gate, n_training_iterations=1, noise_file=""): for _ in range(n_training_iterations): result = alg.train() list_of_results.append(result['hist_stats']) + return alg, list_of_results - # ray.shutdown() - - return alg def return_env_from_alg(alg): env = alg.workers.local_worker().env diff --git a/tests/relaqs/environments/test_gate_synth_env_rllib.py b/tests/relaqs/environments/test_gate_synth_env_rllib.py index 3e1d7c5c..a354f210 100644 --- a/tests/relaqs/environments/test_gate_synth_env_rllib.py +++ b/tests/relaqs/environments/test_gate_synth_env_rllib.py @@ -4,10 +4,13 @@ import sys sys.path.append('../src') from relaqs.save_results import SaveResults -from relaqs.environments.gate_synth_env_rllib import GateSynthEnvRLlib -from relaqs.environments.gate_synth_env_rllib_Haar import GateSynthEnvRLlibHaarNoisy, TwoQubitGateSynth, GateSynthEnvRLlibHaar -from relaqs.api.utils import (run, - return_env_from_alg, +from relaqs.environments.gate_synth_env_rllib_Haar import (GateSynthEnvRLlibHaarNoisy, + TwoQubitGateSynth, + GateSynthEnvRLlibHaar +) +from relaqs.api.utils import (return_env_from_alg, + run_noisless_one_qubit_experiment, + run_noisy_one_qubit_experiment, load_and_analyze_best_unitary ) from relaqs.api.gates import H, I, X @@ -16,19 +19,27 @@ @pytest.fixture() -def config(): +def noisy_config(): + return GateSynthEnvRLlibHaarNoisy.get_default_env_config() + +@pytest.fixture() +def noiseless_config(): return GateSynthEnvRLlibHaar.get_default_env_config() @pytest.fixture() -def gate_environment(config): - return GateSynthEnvRLlibHaar(config) +def noisy_gate_environment(noisy_config): + return GateSynthEnvRLlibHaarNoisy(noisy_config) + +@pytest.fixture() +def noiseless_gate_environment(noiseless_config): + return GateSynthEnvRLlibHaar(noiseless_config) @pytest.fixture() def gate_to_train(): return H() -def test_compute_fidelity_one_qubit_noisy(): - one_qubit_config = GateSynthEnvRLlibHaarNoisy.get_default_env_config() +def test_compute_fidelity_one_qubit_noisy(noisy_config): + one_qubit_config = noisy_config one_qubit_config["U_initial"] = I().get_matrix() one_qubit_config["U_target"] = I().get_matrix() @@ -58,39 +69,39 @@ def test_compute_fidelity_two_qubits(): -def test_reseting_environment(gate_environment, config): +def test_reseting_environment(noiseless_gate_environment, noiseless_config): # reset the environment - gate_environment.reset() - con = config + noiseless_gate_environment.reset() + # test that we initialized unitary correctly - assert np.array_equal(gate_environment.U_initial,con["U_initial"]) + assert np.array_equal(noiseless_gate_environment.U_initial, noiseless_config["U_initial"]) # for the main branch we shall make the X gate the default target gate for testing purposes - assert (X().get_matrix(), gate_environment.U_target) + assert (X().get_matrix(), noiseless_gate_environment.U_target) # Previous fidelity is initially set to zero - assert 0 == gate_environment.prev_fidelity + assert 0 == noiseless_gate_environment.prev_fidelity # There should be initial one observation - assert 9 == len(gate_environment.state) + assert 9 == len(noiseless_gate_environment.state) # Check that the initial flattening happened correctly - assert np.array_equal(np.array([1,0.5, 0, 0.5, 0, 0.5, 1, 0.5]), gate_environment.state[1:9]) + assert np.array_equal(np.array([1,0.5, 0, 0.5, 0, 0.5, 1, 0.5]), noiseless_gate_environment.state[1:9]) -def test_unitarity(gate_environment): +def test_unitarity(noiseless_gate_environment): for _ in range(10): - action = gate_environment.action_space.sample() - gate_environment.step(action) - assert np.allclose(gate_environment.U @ gate_environment.U.T.conjugate(), I().get_matrix()) + action = noiseless_gate_environment.action_space.sample() + noiseless_gate_environment.step(action) + assert np.allclose(noiseless_gate_environment.U @ noiseless_gate_environment.U.T.conjugate(), I().get_matrix()) -def test_training(gate_to_train): +def test_noisy_training(gate_to_train): - n_training_iterations = 250 + n_training_iterations = 1 noise_file = "april/ibmq_belem_month_is_4.json" - alg = run(gate_to_train, + alg,_ = run_noisy_one_qubit_experiment(gate_to_train, n_training_iterations=n_training_iterations, noise_file=noise_file ) @@ -101,7 +112,21 @@ def test_training(gate_to_train): last_100_rows = df.tail(100) fidelities = last_100_rows.iloc[:,0] average_fidelity = sum(fidelities)/len(fidelities) - assert average_fidelity > 0.995 + assert average_fidelity > 0.1 + +def test_noiseless_training(gate_to_train): + n_train_iterations= 1 + alg, _ = run_noisless_one_qubit_experiment(gate_to_train, + n_training_iterations=n_train_iterations + ) + env = return_env_from_alg(alg) + sr = SaveResults(env, alg) + save_dir = sr.save_results() + df = pd.read_csv(save_dir + "env_data.csv") + last_100_rows = df.tail(100) + fidelities = last_100_rows.iloc[:,0] + average_fidelity = sum(fidelities)/len(fidelities) + assert average_fidelity > 0.1 def test_loading_of_unitary(gate_to_train): data_path = RESULTS_DIR + '2023-11-08_11-09-45/env_data.csv' From 08cd5dc1f2e738fdc9647cc5288ecdb41e52688c Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 7 Feb 2024 10:07:44 -0500 Subject: [PATCH 04/31] change to X gate for testing purposes --- scripts/deterministic_agent.py | 10 +++++++--- tests/relaqs/environments/test_gate_synth_env_rllib.py | 10 +++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/scripts/deterministic_agent.py b/scripts/deterministic_agent.py index 8f529cfb..eb8972ff 100644 --- a/scripts/deterministic_agent.py +++ b/scripts/deterministic_agent.py @@ -1,21 +1,23 @@ from relaqs.save_results import SaveResults import numpy as np from relaqs.api.utils import (run, + run_noisy_one_qubit_experiment, sample_noise_parameters, get_best_episode_information, return_env_from_alg ) from relaqs.api.gates import H +from relaqs.plot_data import plot_results, plot_data -n_training_iterations = 1 +n_training_iterations = 300 figure_title ="Inferencing on multiple noisy environments with different detuning noise" noise_file = "april/ibmq_belem_month_is_4.json" noise_file_2 = "april/ibmq_quito_month_is_4.json" path_to_detuning = "qubit_detuning_data.json" # --------------------------> Training of model <----------------------------------------------------- -alg = run(H(), +alg, list_of_results = run_noisy_one_qubit_experiment(H(), n_training_iterations, noise_file=noise_file ) @@ -28,9 +30,11 @@ env.relaxation_rates_list = [np.reciprocal(t1_list).tolist(), np.reciprocal(t2_list).tolist()] env.delta = detuning_list -sr = SaveResults(env, alg) +sr = SaveResults(env, alg, results=list_of_results) save_dir = sr.save_results() print("Results saved to:", save_dir) +plot_data(save_dir, episode_length=alg._episode_history[0].episode_length, figure_title=figure_title) +plot_results(save_dir, figure_title=figure_title) # best_episode_information = get_best_episode_information(save_dir + "env_data.csv") best_episode_information = get_best_episode_information(save_dir + "env_data.pkl") diff --git a/tests/relaqs/environments/test_gate_synth_env_rllib.py b/tests/relaqs/environments/test_gate_synth_env_rllib.py index a354f210..6f3f29e3 100644 --- a/tests/relaqs/environments/test_gate_synth_env_rllib.py +++ b/tests/relaqs/environments/test_gate_synth_env_rllib.py @@ -36,7 +36,7 @@ def noiseless_gate_environment(noiseless_config): @pytest.fixture() def gate_to_train(): - return H() + return X() def test_compute_fidelity_one_qubit_noisy(noisy_config): one_qubit_config = noisy_config @@ -98,7 +98,7 @@ def test_unitarity(noiseless_gate_environment): def test_noisy_training(gate_to_train): - n_training_iterations = 1 + n_training_iterations = 250 noise_file = "april/ibmq_belem_month_is_4.json" alg,_ = run_noisy_one_qubit_experiment(gate_to_train, @@ -112,10 +112,10 @@ def test_noisy_training(gate_to_train): last_100_rows = df.tail(100) fidelities = last_100_rows.iloc[:,0] average_fidelity = sum(fidelities)/len(fidelities) - assert average_fidelity > 0.1 + assert average_fidelity > 0.85 def test_noiseless_training(gate_to_train): - n_train_iterations= 1 + n_train_iterations= 250 alg, _ = run_noisless_one_qubit_experiment(gate_to_train, n_training_iterations=n_train_iterations ) @@ -126,7 +126,7 @@ def test_noiseless_training(gate_to_train): last_100_rows = df.tail(100) fidelities = last_100_rows.iloc[:,0] average_fidelity = sum(fidelities)/len(fidelities) - assert average_fidelity > 0.1 + assert average_fidelity > 0.850 def test_loading_of_unitary(gate_to_train): data_path = RESULTS_DIR + '2023-11-08_11-09-45/env_data.csv' From 8034a3e9e666538353d3c9ccc9e0c548c5d4891a Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 6 Mar 2024 12:25:13 -0500 Subject: [PATCH 05/31] running inferencing multiple times and modified tests --- .github/workflows/python-test.yml | 29 +++++++ scripts/best_fidelities.txt | 1 + scripts/deterministic_agent.py | 6 +- scripts/inferencing.py | 78 ++++++++++++------- .../environments/test_gate_synth_env_rllib.py | 21 +++-- 5 files changed, 99 insertions(+), 36 deletions(-) create mode 100644 .github/workflows/python-test.yml create mode 100644 scripts/best_fidelities.txt diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml new file mode 100644 index 00000000..3d6ed8c6 --- /dev/null +++ b/.github/workflows/python-test.yml @@ -0,0 +1,29 @@ +name: Python Tests + +on: [push, pull_request] + +jobs: + test: + + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: [3.8, 3.9] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + + - name: Run tests + run: | + pytest diff --git a/scripts/best_fidelities.txt b/scripts/best_fidelities.txt new file mode 100644 index 00000000..551be50e --- /dev/null +++ b/scripts/best_fidelities.txt @@ -0,0 +1 @@ +(0.01754923417485625, 0.9963428890697784) \ No newline at end of file diff --git a/scripts/deterministic_agent.py b/scripts/deterministic_agent.py index eb8972ff..e4370b82 100644 --- a/scripts/deterministic_agent.py +++ b/scripts/deterministic_agent.py @@ -6,7 +6,7 @@ get_best_episode_information, return_env_from_alg ) -from relaqs.api.gates import H +from relaqs.api.gates import H, X from relaqs.plot_data import plot_results, plot_data @@ -17,7 +17,7 @@ path_to_detuning = "qubit_detuning_data.json" # --------------------------> Training of model <----------------------------------------------------- -alg, list_of_results = run_noisy_one_qubit_experiment(H(), +alg, list_of_results = run_noisy_one_qubit_experiment(X(), n_training_iterations, noise_file=noise_file ) @@ -25,7 +25,7 @@ # ----------------------- Creating the deterministic agent using actions from the best episode ------------------------------- env = return_env_from_alg(alg) t1_list,t2_list,_ = sample_noise_parameters(noise_file_2, detuning_noise_file=path_to_detuning) -detuning_list = np.random.normal(1e8, 1e4, 9).tolist() +detuning_list = np.random.normal(1e8, 1e8, 9).tolist() # t2_list = np.random.normal(1e-9, 1e-5, 135) env.relaxation_rates_list = [np.reciprocal(t1_list).tolist(), np.reciprocal(t2_list).tolist()] env.delta = detuning_list diff --git a/scripts/inferencing.py b/scripts/inferencing.py index ba7c8893..f2abe177 100644 --- a/scripts/inferencing.py +++ b/scripts/inferencing.py @@ -1,35 +1,59 @@ from relaqs.save_results import SaveResults -from relaqs.plot_data import plot_data +from relaqs.plot_data import plot_data, plot_results import numpy as np -from relaqs.api.utils import do_inferencing, run -from relaqs.api.gates import H - -noise_file = "april/ibmq_belem_month_is_4.json" -inferencing_noise_file = "april/ibmq_manila_month_is_4.json" -n_episodes_for_inferencing = 10 -save = True -plot = True -figure_title = "Inferencing with model" -n_training_iterations = 1 - -# -----------------------> Training model <------------------------ -alg = run(gate=H(), - n_training_iterations=n_training_iterations, +from relaqs.api.utils import do_inferencing, get_best_episode_information +from relaqs.api.gates import X, H +from relaqs.api.utils import ( + run_noisy_one_qubit_experiment, + sample_noise_parameters, + return_env_from_alg +) + +best_fidelities_found = [] +for _ in range(6): + n_training_iterations = 250 + n_episodes_for_inferencing= 400 + figure_title ="Inferencing on multiple noisy environments with different detuning noise for X gate" + noise_file = "april/ibmq_belem_month_is_4.json" + noise_file_2 = "april/ibmq_quito_month_is_4.json" + path_to_detuning = "qubit_detuning_data.json" + + # -----------------------> Training model <------------------------ + alg, list_of_results = run_noisy_one_qubit_experiment(X(), + n_training_iterations, noise_file=noise_file - ) + ) + + # ----------------------- Creating new environment with new detuning ------------------------------- + env = return_env_from_alg(alg) + t1_list,t2_list,_ = sample_noise_parameters(noise_file_2, detuning_noise_file=path_to_detuning) + detuning_list = np.random.normal(1e8, 1e12, 9).tolist() + # t2_list = np.random.normal(1e-9, 1e-5, 135)x + env.relaxation_rates_list = [np.reciprocal(t1_list).tolist(), np.reciprocal(t2_list).tolist()] + env.delta = detuning_list + + # -----------------------> Inferencing <--------------------------- + inferencing_env, inferencing_alg = do_inferencing(alg, n_episodes_for_inferencing,quantum_noise_file_path=noise_file_2) + + # -------------------> Save Inferencing Results <--------------------------------------- + sr = SaveResults(inferencing_env, inferencing_alg) + save_dir = sr.save_results() + print("Results saved to:", save_dir) + # best_episode_information = get_best_episode_information(save_dir + "env_data.csv") + best_episode_information = get_best_episode_information(save_dir + "env_data.pkl") + + print("Fidelities from best epsiode: ", [best_episode_information.iloc[0,0], best_episode_information.iloc[1,0]]) + best_fidelities_found.append((best_episode_information.iloc[0,0],best_episode_information.iloc[1,0] )) + best_fidelity_tuple = str((best_episode_information.iloc[0,0],best_episode_information.iloc[1,0])) + best_fidelity_file = "best_fidelities.txt" + with open(save_dir + best_fidelity_file, 'w') as file: + file.write(best_fidelity_tuple) -# -----------------------> Inferencing <--------------------------- -env, alg = do_inferencing(alg, n_episodes_for_inferencing,quantum_noise_file_path=inferencing_noise_file) -# -------------------> Save Inferencing Results <--------------------------------------- -sr = SaveResults(env, alg) -save_dir = sr.save_results() -print("Results saved to:", save_dir) + # ---------------------> Plot Data <------------------------------------------- + plot_data(save_dir, episode_length=inferencing_alg._episode_history[0].episode_length, figure_title=figure_title) + -# ---------------------> Plot Data <------------------------------------------- -assert save is True, "If plot=True, then save must also be set to True" +print(best_fidelities_found) -plot_data(save_dir, episode_length=alg._episode_history[0].episode_length, figure_title=figure_title) -print("Plots Created") -# -------------------------------------------------------------- diff --git a/tests/relaqs/environments/test_gate_synth_env_rllib.py b/tests/relaqs/environments/test_gate_synth_env_rllib.py index 6f3f29e3..28facff3 100644 --- a/tests/relaqs/environments/test_gate_synth_env_rllib.py +++ b/tests/relaqs/environments/test_gate_synth_env_rllib.py @@ -30,13 +30,20 @@ def noiseless_config(): def noisy_gate_environment(noisy_config): return GateSynthEnvRLlibHaarNoisy(noisy_config) +@pytest.fixture() +def number_of_training_iterations(): + return 1 + @pytest.fixture() def noiseless_gate_environment(noiseless_config): return GateSynthEnvRLlibHaar(noiseless_config) @pytest.fixture() -def gate_to_train(): - return X() +def gate_to_train(request): + if request.param == 'x': + return X() + elif request.param == 'h': + return H() def test_compute_fidelity_one_qubit_noisy(noisy_config): one_qubit_config = noisy_config @@ -96,9 +103,9 @@ def test_unitarity(noiseless_gate_environment): noiseless_gate_environment.step(action) assert np.allclose(noiseless_gate_environment.U @ noiseless_gate_environment.U.T.conjugate(), I().get_matrix()) -def test_noisy_training(gate_to_train): +def test_noisy_training(gate_to_train, number_of_training_iterations): - n_training_iterations = 250 + n_training_iterations = number_of_training_iterations noise_file = "april/ibmq_belem_month_is_4.json" alg,_ = run_noisy_one_qubit_experiment(gate_to_train, @@ -114,8 +121,9 @@ def test_noisy_training(gate_to_train): average_fidelity = sum(fidelities)/len(fidelities) assert average_fidelity > 0.85 -def test_noiseless_training(gate_to_train): - n_train_iterations= 250 +@pytest.mark.parametrize("gate_to_train", ['x', 'h'], indirect=True) +def test_noiseless_training(gate_to_train, number_of_training_iterations): + n_train_iterations= number_of_training_iterations alg, _ = run_noisless_one_qubit_experiment(gate_to_train, n_training_iterations=n_train_iterations ) @@ -128,6 +136,7 @@ def test_noiseless_training(gate_to_train): average_fidelity = sum(fidelities)/len(fidelities) assert average_fidelity > 0.850 +@pytest.mark.parametrize("gate_to_train", ['x'], indirect=True) def test_loading_of_unitary(gate_to_train): data_path = RESULTS_DIR + '2023-11-08_11-09-45/env_data.csv' load_and_analyze_best_unitary(data_path, gate_to_train) From e9a9cb0c15b76707fb6ad0fb880a3d01f68f1047 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 6 Mar 2024 13:22:37 -0500 Subject: [PATCH 06/31] added a python-test yaml file in order to run remote tests --- .github/workflows/python-test.yml | 2 +- requirements.txt | 6 ++++++ tests/relaqs/environments/test_gate_synth_env_rllib.py | 4 +++- tests/scripts/__init__.py | 6 ------ tests/scripts/test_custom_logging.py | 3 --- 5 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 requirements.txt delete mode 100644 tests/scripts/__init__.py delete mode 100644 tests/scripts/test_custom_logging.py diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 3d6ed8c6..54f74f82 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -26,4 +26,4 @@ jobs: - name: Run tests run: | - pytest + pytest tests/relaqs diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..62dc6f31 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +ray[rllib] +ray[tune] +matplotlib +qutip +torch +tensorboard \ No newline at end of file diff --git a/tests/relaqs/environments/test_gate_synth_env_rllib.py b/tests/relaqs/environments/test_gate_synth_env_rllib.py index 28facff3..40a8d9c9 100644 --- a/tests/relaqs/environments/test_gate_synth_env_rllib.py +++ b/tests/relaqs/environments/test_gate_synth_env_rllib.py @@ -8,6 +8,7 @@ TwoQubitGateSynth, GateSynthEnvRLlibHaar ) + from relaqs.api.utils import (return_env_from_alg, run_noisless_one_qubit_experiment, run_noisy_one_qubit_experiment, @@ -32,7 +33,7 @@ def noisy_gate_environment(noisy_config): @pytest.fixture() def number_of_training_iterations(): - return 1 + return 250 @pytest.fixture() def noiseless_gate_environment(noiseless_config): @@ -103,6 +104,7 @@ def test_unitarity(noiseless_gate_environment): noiseless_gate_environment.step(action) assert np.allclose(noiseless_gate_environment.U @ noiseless_gate_environment.U.T.conjugate(), I().get_matrix()) +@pytest.mark.parametrize("gate_to_train", ['x'], indirect=True) def test_noisy_training(gate_to_train, number_of_training_iterations): n_training_iterations = number_of_training_iterations diff --git a/tests/scripts/__init__.py b/tests/scripts/__init__.py deleted file mode 100644 index cbe29157..00000000 --- a/tests/scripts/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# This is necessary to allow pytest to correctly find the scripts folder -# Potentially less hacky option: make the scripts folder a module to be installed -import pytest -import sys -import os -sys.path.insert(1, os.path.abspath('./scripts/')) diff --git a/tests/scripts/test_custom_logging.py b/tests/scripts/test_custom_logging.py deleted file mode 100644 index eebfc19a..00000000 --- a/tests/scripts/test_custom_logging.py +++ /dev/null @@ -1,3 +0,0 @@ -from custom_logging import run - - From 9b02c28c89c04c56448bae4aa4fc80e77a5445df Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 6 Mar 2024 14:51:46 -0500 Subject: [PATCH 07/31] working on getting github to install package for remote testing --- .github/workflows/python-test.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 54f74f82..8ed84d20 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -23,7 +23,11 @@ jobs: python -m pip install --upgrade pip pip install pytest if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - + + - name: Install the package + run: | + pip install . + - name: Run tests run: | pytest tests/relaqs From 5d7da268db93d8b9f70def8714f7c01e06847e06 Mon Sep 17 00:00:00 2001 From: Amara Katabarwa Date: Wed, 6 Mar 2024 16:17:30 -0500 Subject: [PATCH 08/31] Create 2023-11-08_11-09-45 --- results/2023-11-08_11-09-45 | 1 + 1 file changed, 1 insertion(+) create mode 100644 results/2023-11-08_11-09-45 diff --git a/results/2023-11-08_11-09-45 b/results/2023-11-08_11-09-45 new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/results/2023-11-08_11-09-45 @@ -0,0 +1 @@ + From e58267cdf70374260bd5a37995eb50628343de4a Mon Sep 17 00:00:00 2001 From: Amara Katabarwa Date: Wed, 6 Mar 2024 16:18:09 -0500 Subject: [PATCH 09/31] Delete results/2023-11-08_11-09-45 --- results/2023-11-08_11-09-45 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 results/2023-11-08_11-09-45 diff --git a/results/2023-11-08_11-09-45 b/results/2023-11-08_11-09-45 deleted file mode 100644 index 8b137891..00000000 --- a/results/2023-11-08_11-09-45 +++ /dev/null @@ -1 +0,0 @@ - From 988b8eac205dc890354df7e51ae377badf6b127c Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 6 Mar 2024 16:59:42 -0500 Subject: [PATCH 10/31] removing a test that requires uploading a huge file --- tests/relaqs/environments/test_gate_synth_env_rllib.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/relaqs/environments/test_gate_synth_env_rllib.py b/tests/relaqs/environments/test_gate_synth_env_rllib.py index 40a8d9c9..49bc3306 100644 --- a/tests/relaqs/environments/test_gate_synth_env_rllib.py +++ b/tests/relaqs/environments/test_gate_synth_env_rllib.py @@ -138,10 +138,10 @@ def test_noiseless_training(gate_to_train, number_of_training_iterations): average_fidelity = sum(fidelities)/len(fidelities) assert average_fidelity > 0.850 -@pytest.mark.parametrize("gate_to_train", ['x'], indirect=True) -def test_loading_of_unitary(gate_to_train): - data_path = RESULTS_DIR + '2023-11-08_11-09-45/env_data.csv' - load_and_analyze_best_unitary(data_path, gate_to_train) +# @pytest.mark.parametrize("gate_to_train", ['x'], indirect=True) +# def test_loading_of_unitary(gate_to_train): +# data_path = RESULTS_DIR + '2023-11-08_11-09-45/env_data.csv' +# load_and_analyze_best_unitary(data_path, gate_to_train) From d7d8ae5dc8f35db83909b1c62061fff70aab099d Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 6 Mar 2024 17:12:47 -0500 Subject: [PATCH 11/31] adding line so that installation adds json files --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index 2ca4fd0a..5d13eedd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,6 +17,9 @@ install_requires = [options.packages.find] where = src + +[options.package_data] +* = *.json [bdist_wheel] universal = 1 From df9f219ce6a7f362f62ad56eaafb21a0fdded14c Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 6 Mar 2024 17:19:26 -0500 Subject: [PATCH 12/31] making sure github installs a certain version of ray 2.4.0 to avoid deprecation warnings and errors --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 62dc6f31..54b0ad30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ray[rllib] +ray[rllib]==2.4.0 ray[tune] matplotlib qutip From 7f5a667d0ed14a5ebfddd7fb8d673fed1a6cda56 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 6 Mar 2024 18:38:44 -0500 Subject: [PATCH 13/31] added -v to show output --- .github/workflows/python-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 8ed84d20..409e97fc 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -30,4 +30,4 @@ jobs: - name: Run tests run: | - pytest tests/relaqs + pytest tests/relaqs -v From df726a3e60c6ec027e579dc69e7f752f0f74f53e Mon Sep 17 00:00:00 2001 From: akataba Date: Fri, 8 Mar 2024 16:38:45 -0500 Subject: [PATCH 14/31] removing workflow for remote testing that seems to be installing ray that is incompaptible with how we have written the code base so far --- .github/workflows/python-package.yml | 42 ---------------------------- 1 file changed, 42 deletions(-) delete mode 100644 .github/workflows/python-package.yml diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml deleted file mode 100644 index e0137a41..00000000 --- a/.github/workflows/python-package.yml +++ /dev/null @@ -1,42 +0,0 @@ -# This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python - -name: test relaqs - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ["3.10"] - - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install wheel setuptools - python -m pip install . - python -m pip install flake8 pytest - #if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - # - name: Lint with flake8 - # run: | - # # stop the build if there are Python syntax errors or undefined names - # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with pytest - run: | - pytest From e28c8dd98d7126bbf87fe95189141d3bf952bdae Mon Sep 17 00:00:00 2001 From: akataba Date: Fri, 8 Mar 2024 17:05:30 -0500 Subject: [PATCH 15/31] remove the old gate environments and fixing the issue of circular imports when running tests --- src/relaqs/api/__init__.py | 2 +- src/relaqs/api/utils.py | 15 +- .../environments/gate_synth_env_rllib_Haar.py | 623 ------------------ .../environments/test_gate_synth_env_rllib.py | 27 +- 4 files changed, 19 insertions(+), 648 deletions(-) delete mode 100644 src/relaqs/environments/gate_synth_env_rllib_Haar.py diff --git a/src/relaqs/api/__init__.py b/src/relaqs/api/__init__.py index a9313b39..8d1478f1 100644 --- a/src/relaqs/api/__init__.py +++ b/src/relaqs/api/__init__.py @@ -1,4 +1,4 @@ from .training import TrainRLLib from .callbacks import GateSynthesisCallbacks from .gates import Gate -from .utils import gate_fidelity, dm_fidelity, load_pickled_env_data +# from .utils import gate_fidelity, dm_fidelity, load_pickled_env_data diff --git a/src/relaqs/api/utils.py b/src/relaqs/api/utils.py index cee4c369..00b01d62 100644 --- a/src/relaqs/api/utils.py +++ b/src/relaqs/api/utils.py @@ -7,7 +7,8 @@ from relaqs import RESULTS_DIR import ast from ray.tune.registry import register_env -from relaqs.environments.gate_synth_env_rllib_Haar import GateSynthEnvRLlibHaarNoisy, GateSynthEnvRLlibHaar +from relaqs.environments.single_qubit_env import SingleQubitEnv +from relaqs.environments.noisy_single_qubit_env import NoisySingleQubitEnv from relaqs.quantum_noise_data.get_data import (get_month_of_all_qubit_data, get_single_qubit_detuning) from relaqs.api.callbacks import GateSynthesisCallbacks from relaqs import QUANTUM_NOISE_DATA_DIR @@ -88,10 +89,10 @@ def get_best_episode_information(filename): return best_episodes def noisy_env_creator(config): - return GateSynthEnvRLlibHaarNoisy(config) + return NoisySingleQubitEnv(config) def noiseless_env_creator(config): - return GateSynthEnvRLlibHaar(config) + return SingleQubitEnv(config) def run(env_class, gate, n_training_iterations=1, noise_file=""): """Args @@ -149,7 +150,7 @@ def run_noisless_one_qubit_experiment(gate,n_training_iterations=1): """ register_env("my_env", noiseless_env_creator) - env_config = GateSynthEnvRLlibHaar.get_default_env_config() + env_config = SingleQubitEnv.get_default_env_config() env_config["U_target"] = gate.get_matrix() # env_config["observation_space_size"] = 2*16 + 1 # 2*16 = (complex number)*(density matrix elements = 4)^2, + 1 for fidelity env_config["verbose"] = True @@ -160,7 +161,7 @@ def run_noisless_one_qubit_experiment(gate,n_training_iterations=1): alg_config.environment("my_env", env_config=env_config) alg_config.rollouts(batch_mode="complete_episodes") alg_config.callbacks(GateSynthesisCallbacks) - alg_config.train_batch_size = GateSynthEnvRLlibHaar.get_default_env_config()["steps_per_Haar"] + alg_config.train_batch_size = SingleQubitEnv.get_default_env_config()["steps_per_Haar"] alg_config.actor_lr = 4e-5 alg_config.critic_lr = 5e-4 @@ -190,7 +191,7 @@ def run_noisy_one_qubit_experiment(gate, n_training_iterations=1, noise_file=" """ register_env("my_env", noisy_env_creator) - env_config = GateSynthEnvRLlibHaarNoisy.get_default_env_config() + env_config = NoisySingleQubitEnv.get_default_env_config() env_config["U_target"] = gate.get_matrix() # ---------------------> Get quantum noise data <------------------------- t1_list, t2_list, detuning_list = sample_noise_parameters(noise_file) @@ -206,7 +207,7 @@ def run_noisy_one_qubit_experiment(gate, n_training_iterations=1, noise_file=" alg_config.environment("my_env", env_config=env_config) alg_config.rollouts(batch_mode="complete_episodes") alg_config.callbacks(GateSynthesisCallbacks) - alg_config.train_batch_size = GateSynthEnvRLlibHaarNoisy.get_default_env_config()["steps_per_Haar"] + alg_config.train_batch_size = NoisySingleQubitEnv.get_default_env_config()["steps_per_Haar"] alg_config.actor_lr = 4e-5 alg_config.critic_lr = 5e-4 diff --git a/src/relaqs/environments/gate_synth_env_rllib_Haar.py b/src/relaqs/environments/gate_synth_env_rllib_Haar.py deleted file mode 100644 index f666ac60..00000000 --- a/src/relaqs/environments/gate_synth_env_rllib_Haar.py +++ /dev/null @@ -1,623 +0,0 @@ -import gymnasium as gym -import numpy as np -import scipy.linalg as la -import cmath -import random -from qutip.superoperator import liouvillian, spre, spost -from qutip import Qobj, tensor -from qutip.operators import * -from qutip.qip.operations import cnot, cphase -#from relaqs.api.reward_functions import negative_matrix_difference_norm - -sig_p = np.array([[0, 1], [0, 0]]) -sig_m = np.array([[0, 0], [1, 0]]) -X = np.array([[0, 1], [1, 0]]) -Z = np.array([[1, 0], [0, -1]]) -I = np.array([[1, 0], [0, 1]]) -Y = np.array([[0, 1j], [-1j, 0]]) - - -#two-qubit single qubit gates -II = tensor(Qobj(I),Qobj(I)).data.toarray() -X1 = tensor(Qobj(X),Qobj(I)).data.toarray() -X2 = tensor(Qobj(I),Qobj(X)).data.toarray() -Y1 = tensor(Qobj(Y),Qobj(I)).data.toarray() -Y2 = tensor(Qobj(I),Qobj(Y)).data.toarray() -Z1 = tensor(Qobj(Z),Qobj(I)).data.toarray() -Z2 = tensor(Qobj(I),Qobj(Z)).data.toarray() - -sig_p1 = tensor(Qobj(sig_p),Qobj(I)).data.toarray() -sig_p2 = tensor(Qobj(I),Qobj(sig_p)).data.toarray() -sig_m1 = tensor(Qobj(sig_m),Qobj(I)).data.toarray() -sig_m2 = tensor(Qobj(I),Qobj(sig_m)).data.toarray() -sigmap1 = Qobj(sig_p1) -sigmap2 = Qobj(sig_p2) -sigmam1 = Qobj(sig_m1) -sigmam2 = Qobj(sig_m2) - -#two-qubit gate basis -XX = tensor(Qobj(X),Qobj(X)).data.toarray() -YY = tensor(Qobj(Y),Qobj(Y)).data.toarray() -ZZ = tensor(Qobj(Z),Qobj(Z)).data.toarray() -exchangeOperator = tensor(Qobj(sig_p),Qobj(sig_m)).data.toarray() + tensor(Qobj(sig_m),Qobj(sig_p)).data.toarray() - -CNOT = cnot().data.toarray() -CZ = cphase(np.pi).data.toarray() - -class GateSynthEnvRLlibHaar(gym.Env): - @classmethod - def get_default_env_config(cls): - return { - "action_space_size": 3, - "U_initial": I, - "U_target": X, - "final_time": 35.5556E-9, # in seconds - "num_Haar_basis": 1, - "steps_per_Haar": 2, # steps per Haar basis per episode - "delta": 0, - "save_data_every_step": 1, # unused? - "verbose": True, - "observation_space_size": 9, # 1 (fidelity) + 8 (flattened unitary) - } - def __init__(self, env_config): - self.final_time = env_config["final_time"] # Final time for the gates - self.observation_space = gym.spaces.Box(low=-1, high=1, shape=(env_config["observation_space_size"],)) - self.action_space = gym.spaces.Box(low=np.array([-1, -1, -1]), high=np.array([1, 1, 1])) - self.delta = env_config["delta"] # detuning - self.U_target = env_config["U_target"] - self.U_initial = env_config["U_initial"] # future todo, can make random initial state - self.U = env_config["U_initial"] - self.num_Haar_basis = env_config["num_Haar_basis"] - self.steps_per_Haar = env_config["steps_per_Haar"] - self.verbose = env_config["verbose"] - self.current_Haar_num = 1 - self.current_step_per_Haar = 1 - self.H_array = [] - self.H_tot = [] - self.U_array = [] - self.state = self.unitary_to_observation(self.U) - self.prev_fidelity = 0 - self.gamma_phase_max = 1.1675 * np.pi - self.gamma_magnitude_max = 1.8 * np.pi / self.final_time / self.steps_per_Haar - self.gamma_detuning_max = 0.05E9 #detuning of the control pulse in Hz - self.transition_history = [] - self.episode_id = 0 - - def unitary_to_observation(self, U): - return ( - np.array( - [(abs(x), (cmath.phase(x) / np.pi + 1) / 2) for x in U.flatten()], - dtype=np.float64, - ) - .squeeze() - .reshape(-1) - ) - - def get_observation(self): - return np.append([self.compute_fidelity()], self.unitary_to_observation(self.U)) - - def compute_fidelity(self): - return float(np.abs(np.trace(self.U_target.conjugate().transpose() @ self.U))) / (self.U.shape[0]) - - def hamiltonian(self, delta, alpha, gamma_magnitude, gamma_phase): - """Alpha and gamma are complex. This function could be made a callable class attribute.""" - return (delta + alpha) * Z + gamma_magnitude * (np.cos(gamma_phase) * X + np.sin(gamma_phase) * Y) - - def reset(self, *, seed=None, options=None): - self.U = self.U_initial - starting_observeration = self.get_observation() - self.state = self.get_observation() - self.current_Haar_num = 1 - self.current_step_per_Haar = 1 - self.H_array = [] - self.H_tot = [] - self.U_array = [] - self.prev_fidelity = 0 - info = {} - self.episode_id += 1 - return starting_observeration, info - - def step(self, action): - num_time_bins = 2 ** (self.current_Haar_num - 1) # Haar number decides the number of time bins - - # gamma is the complex amplitude of the control field - gamma_magnitude = self.gamma_magnitude_max / 2 * (action[0] + 1) - gamma_phase = self.gamma_phase_max * action[1] - alpha = self.gamma_detuning_max * action[2] - - H = self.hamiltonian(self.delta, alpha, gamma_magnitude, gamma_phase) - self.H_array.append(H) - - self.H_tot = [] - - for ii, H_elem in enumerate(self.H_array): - for jj in range(0, num_time_bins): - Haar_num = self.current_Haar_num - np.floor(ii / self.steps_per_Haar) # Haar_num: label which Haar wavelet, current_Haar_num: order in the array - factor = (-1) ** np.floor(jj / (2 ** (Haar_num - 1))) - if ii > 0: - self.H_tot[jj] += factor * H_elem - else: - self.H_tot.append(factor * H_elem) - - self.U = self.U_initial - - for jj in range(0, num_time_bins): - Ut = la.expm(-1j * self.final_time / num_time_bins * self.H_tot[jj]) - self.U = Ut @ self.U - - self.U_array.append(self.U) - - # Get reward (fidelity) - fidelity = self.compute_fidelity() - reward = (-3 * np.log10(1.0 - fidelity) + np.log10(1.0 - self.prev_fidelity)) + (3 * fidelity - self.prev_fidelity) - self.prev_fidelity = fidelity - - self.state = self.get_observation() - - # printing on the command line for quick viewing - if self.verbose is True: - print( - "Step: ", f"{self.current_step_per_Haar}", - "F: ", f"{fidelity:7.3f}", - "R: ", f"{reward:7.3f}", - "amp: " f"{action[0]:7.3f}", - "phase: " f"{action[1]:7.3f}", - "detuning: " f"{action[2]:7.3f}" - ) - - self.transition_history.append([fidelity, reward, action, self.U, self.episode_id]) - - # Determine if episode is over - truncated = False - terminated = False - if fidelity >= 1: - truncated = True # truncated when target fidelity reached - elif (self.current_Haar_num >= self.num_Haar_basis) and (self.current_step_per_Haar >= self.steps_per_Haar): # terminate when all Haar is tested - terminated = True - else: - terminated = False - - if (self.current_step_per_Haar == self.steps_per_Haar): # For each Haar basis, if all trial steps ends, them move to next haar wavelet - self.current_Haar_num += 1 - self.current_step_per_Haar = 1 - else: - self.current_step_per_Haar += 1 - - info = {} - return (self.state, reward, terminated, truncated, info) - - -class GateSynthEnvRLlibHaarNoisy(gym.Env): - @classmethod - def get_default_env_config(cls): - return { - "action_space_size": 3, - #"action_space_size": 2, - "U_initial": I, # staring with I - "U_target": X, # target for X - "final_time": 35.5556E-9, # in seconds - "num_Haar_basis": 1, # number of Haar basis (need to update for odd combinations) - "steps_per_Haar": 2, # steps per Haar basis per episode - "delta": [0], # qubit detuning - "save_data_every_step": 1, - "verbose": True, -# "relaxation_rates_list": [[0.01,0.02],[0.05, 0.07]], # relaxation lists of list of floats to be sampled from when resetting environment. -# "relaxation_ops": [sigmam(),sigmaz()] #relaxation operator lists for T1 and T2, respectively - "relaxation_rates_list": [[314159]], # relaxation lists of list of floats to be sampled from when resetting environment. (10 usec) - "relaxation_ops": [sigmam()], #relaxation operator lists for T1 and T2, respectively -# "observation_space_size": 35, # 2*16 = (complex number)*(density matrix elements = 4)^2, + 1 for fidelity + 2 for relaxation rate - "observation_space_size": 2*16 + 1 + 1 + 1 # 2*16 = (complex number)*(density matrix elements = 4)^2, + 1 for fidelity + 1 for relaxation rate + 1 for detuning - } - - def __init__(self, env_config): - self.final_time = env_config["final_time"] # Final time for the gates - self.observation_space = gym.spaces.Box(low=0, high=1, shape=(env_config["observation_space_size"],)) # propagation operator elements + fidelity - self.action_space = gym.spaces.Box(low=np.array([-1, -1, -1]), high=np.array([1, 1, 1])) # for detuning included control - #self.action_space = gym.spaces.Box(low=np.array([-1, -1]), high=np.array([1, 1])) -# self.delta = [env_config["delta"]] # detuning - self.delta = env_config["delta"] # detuning - self.detuning = 0 - self.detuning_update() # ??? - self.U_target = self.unitary_to_superoperator(env_config["U_target"]) - self.U_initial = self.unitary_to_superoperator(env_config["U_initial"]) - self.num_Haar_basis = env_config["num_Haar_basis"] - self.steps_per_Haar = env_config["steps_per_Haar"] - self.verbose = env_config["verbose"] - self.relaxation_rates_list = env_config["relaxation_rates_list"] - self.relaxation_ops = env_config["relaxation_ops"] - self.relaxation_rate = self.get_relaxation_rate() - self.current_Haar_num = 1 # starting with 1 - self.current_step_per_Haar = 1 - self.H_array = [] # saving all H's with Haar wavelet to be multiplied - self.H_tot = [] # Haar wavelet multipied H summed up for each time bin - self.L_array = [] # Liouvillian for each time bin - self.U_array = [] # propagation operators for each time bin - self.U = self.U_initial.copy() # multiplied propagtion operators - self.state = self.unitary_to_observation(self.U_initial) # starting observation space - self.prev_fidelity = 0 # previous step' fidelity for rewarding - self.gamma_phase_max = 1.1675 * np.pi - self.gamma_magnitude_max = 1.8 * np.pi / self.final_time / self.steps_per_Haar - self.gamma_detuning_max = 0.05E9 #detuning of the control pulse in Hz - self.transition_history = [] - self.episode_id = 0 - - def detuning_update(self): - # Random detuning selection - if len(self.delta)==1: - self.detuning = self.delta[0] - else: - self.detuning = random.sample(self.delta,k=1)[0] - print("detuning: ", f"{self.detuning}") - - - def unitary_to_superoperator(self, U): - return (spre(Qobj(U)) * spost(Qobj(U))).data.toarray() - - def get_relaxation_rate(self): - relaxation_size = len(self.relaxation_ops) #get number of relaxation ops - - sampled_rate_list = [] - for ii in range(relaxation_size): - sampled_rate_list.append(random.sample(self.relaxation_rates_list[ii],k=1)[0]) - - return sampled_rate_list - - def get_observation(self): - normalizedDetuning = [(self.detuning - min(self.delta)+1E-15)/(max(self.delta)-min(self.delta)+1E-15)] - return np.append([self.compute_fidelity()]+[x//6283185 for x in self.relaxation_rate]+normalizedDetuning, self.unitary_to_observation(self.U)) #6283185 assuming 500 nanosecond relaxation is max - - def compute_fidelity(self): - U_target_dagger = self.U_target.conjugate().transpose() - return float(np.abs(np.trace(U_target_dagger @ self.U))) / (self.U.shape[0]) - - def unitary_to_observation(self, U): - return ( - np.array( - [(abs(x), (cmath.phase(x) / np.pi + 1) / 2) for x in U.flatten()], - dtype=np.float64, - ) - .squeeze() - .reshape(-1) # cmath phase gives -pi to pi - ) - - def hamiltonian(self, delta, alpha, gamma_magnitude, gamma_phase): - """Alpha and gamma are complex. This function could be made a callable class attribute.""" - return (delta + alpha) * Z + gamma_magnitude * (np.cos(gamma_phase) * X + np.sin(gamma_phase) * Y) - - def reset(self, *, seed=None, options=None): - self.U = self.U_initial.copy() - self.state = self.get_observation() - self.current_Haar_num = 1 - self.current_step_per_Haar = 1 - self.H_array = [] - self.H_tot = [] - self.L_array = [] - self.U_array = [] - self.prev_fidelity = 0 - self.episode_id += 1 - self.relaxation_rate = self.get_relaxation_rate() - self.detuning = 0 - self.detuning_update() - starting_observeration = self.get_observation() - info = {} - return starting_observeration, info - - def step(self, action): - num_time_bins = 2 ** (self.current_Haar_num - 1) # Haar number decides the number of time bins - - # action space setting - alpha = 0 # in current simulation we do not adjust the detuning - - # gamma is the complex amplitude of the control field - gamma_magnitude = self.gamma_magnitude_max / 2 * (action[0] + 1) - gamma_phase = self.gamma_phase_max * action[1] - alpha = self.gamma_detuning_max * action[2] - - # Set noise opertors - jump_ops = [] - for ii in range(len(self.relaxation_ops)): - jump_ops.append(np.sqrt(self.relaxation_rate[ii]) * self.relaxation_ops[ii]) - - # Hamiltonian with controls - H = self.hamiltonian(self.detuning, alpha, gamma_magnitude, gamma_phase) - self.H_array.append(H) # Array of Hs at each Haar wavelet - - # H_tot for adding Hs at each time bins - self.H_tot = [] - - for ii, H_elem in enumerate(self.H_array): - for jj in range(0, num_time_bins): - Haar_num = self.current_Haar_num - np.floor(ii / self.steps_per_Haar) # Haar_num: label which Haar wavelet, current_Haar_num: order in the array - factor = (-1) ** np.floor(jj / (2 ** (Haar_num - 1))) # factor flips the sign every 2^(Haar_num-1) - if ii > 0: - self.H_tot[jj] += factor * H_elem - else: # Because H_tot[jj] does not exist - self.H_tot.append(factor * H_elem) - - self.L = ([]) # at every step we calculate L again because minimal time bin changes - self.U = np.eye(4) # identity - - for jj in range(0, num_time_bins): - L = (liouvillian(Qobj(self.H_tot[jj]), jump_ops, data_only=False, chi=None)).data.toarray() # Liouvillian calc - self.L_array.append(L) - Ut = la.expm(self.final_time / num_time_bins * L) # time evolution (propagation operator) - self.U = Ut @ self.U # calculate total propagation until the time we are at - - # Reward and fidelity calculation - fidelity = self.compute_fidelity() - reward = (-3 * np.log10(1.0 - fidelity) + np.log10(1.0 - self.prev_fidelity)) + (3 * fidelity - self.prev_fidelity) - #reward = negative_matrix_difference_norm(self.U_target, self.U) - self.prev_fidelity = fidelity - - self.state = self.get_observation() - - # printing on the command line for quick viewing - if self.verbose is True: - print( - "Step: ", f"{self.current_step_per_Haar}" + " episode id :" + f"{self.episode_id}", - "Relaxation rates:") - for rate in self.relaxation_rate: - print(f"{rate:7.6f}") - print( - "F: ", f"{fidelity:7.3f}", - "R: ", f"{reward:7.3f}", - "amp: " f"{action[0]:7.3f}", - "phase: " f"{action[1]:7.3f}", - ) - - self.transition_history.append([fidelity, reward, action, self.U, self.episode_id]) - - # Determine if episode is over - truncated = False - terminated = False - if fidelity >= 1: - truncated = True # truncated when target fidelity reached - elif (self.current_Haar_num >= self.num_Haar_basis) and (self.current_step_per_Haar >= self.steps_per_Haar): # terminate when all Haar is tested - terminated = True - else: - terminated = False - - if (self.current_step_per_Haar == self.steps_per_Haar): # For each Haar basis, if all trial steps ends, them move to next haar wavelet - self.current_Haar_num += 1 - self.current_step_per_Haar = 1 - else: - self.current_step_per_Haar += 1 - - info = {} - return (self.state, reward, terminated, truncated, info) - - -class TwoQubitGateSynth(gym.Env): - @classmethod - def get_default_env_config(cls): - return { - "action_space_size": 7, - "U_initial": II, # staring with I - "U_target": CZ, # target for CZ - "final_time": 30E-9, # in seconds - "num_Haar_basis": 4, # number of Haar basis (need to update for odd combinations) - "steps_per_Haar": 2, # steps per Haar basis per episode - "delta": [[0],[0]], # qubit detuning - "save_data_every_step": 1, - "verbose": True, -# "relaxation_rates_list": [[1/60E-6/2/np.pi],[1/30E-6/2/np.pi],[1/66E-6/2/np.pi],[1/5E-6/2/np.pi]], # relaxation lists of list of floats to be sampled from when resetting environment. - "relaxation_rates_list": [[0],[0],[0],[0]], # for now - "relaxation_ops": [sigmam1,sigmam2,Qobj(Z1),Qobj(Z2)], #relaxation operator lists for T1 and T2, respectively -# "observation_space_size": 35, # 2*16 = (complex number)*(density matrix elements = 4)^2, + 1 for fidelity + 2 for relaxation rate - "observation_space_size": 2*256 + 1 + 4 + 2 # 2*16 = (complex number)*(density matrix elements = 4)^2, + 1 for fidelity + 4 for relaxation rate + 2 for detuning - } - - #physics: https://journals.aps.org/prapplied/pdf/10.1103/PhysRevApplied.10.054062, eq(2) - #parameters: https://journals.aps.org/prx/pdf/10.1103/PhysRevX.11.021058 - #30 ns duration, g1 = 72.5 MHz, g2 = 71.5 MHz, g12 = 5 MHz - #T1 = 60 us, 30 us - #T2* = 66 us, 5 us - - def hamiltonian(self, delta1, delta2, alpha1, alpha2, twoQubitDetuning, gamma_magnitude1, gamma_phase1, gamma_magnitude2, gamma_phase2, g1 = 72.5E6, g2 = 71.5E6, g12 = 5E6): - selfEnergyTerms = (delta1 + alpha1) * Z1 + (delta2 + alpha2) * Z2 - Qubit1ControlTerms = gamma_magnitude1 * (np.cos(gamma_phase1) * X1 + np.sin(gamma_phase1) * Y1) - Qubit2ControlTerms = gamma_magnitude2 * (np.cos(gamma_phase2) * X2 + np.sin(gamma_phase2) * Y2) - - #omega1 = delta1+alpha1, omega2 = delta2+alpha2, omegaC = alphaC -# Delta1 = delta1+alpha1-alphaC -# Delta2 = delta2+alpha2-alphaC -# twoQubitDetuning = 1/((1/Delta1 + 1/Delta2)/2) - - g_eff = g1*g2/twoQubitDetuning + g12 - interactionEnergy = g_eff*exchangeOperator - - energyTotal = selfEnergyTerms + interactionEnergy + Qubit1ControlTerms + Qubit2ControlTerms - - -# print("coupling: ", f"{g_eff:7.3f}") -# print("Delta1: ", f"{Delta1:7.3f}") -# print("Delta2: ", f"{Delta2:7.3f}") -# print("twoQubitDetuning: ", f"{twoQubitDetuning:7.3f}") - - return energyTotal - - def __init__(self, env_config): - self.final_time = env_config["final_time"] # Final time for the gates - self.observation_space = gym.spaces.Box(low=0, high=1, shape=(env_config["observation_space_size"],)) # propagation operator elements + fidelity + relaxation + detuning - self.action_space = gym.spaces.Box(low=-1*np.ones(7), high=np.ones(7)) #alpha1, alpha2, alphaC, gamma_magnitude1, gamma_phase1, gamma_magnitude2, gamma_phase2 - self.delta = env_config["delta"] # detuning - self.detuning = [0, 0] - self.detuning_update() - self.U_target = self.unitary_to_superoperator(env_config["U_target"]) - self.U_initial = self.unitary_to_superoperator(env_config["U_initial"]) - self.num_Haar_basis = env_config["num_Haar_basis"] - self.steps_per_Haar = env_config["steps_per_Haar"] - self.verbose = env_config["verbose"] - self.relaxation_rates_list = env_config["relaxation_rates_list"] - self.relaxation_ops = env_config["relaxation_ops"] - self.relaxation_rate = self.get_relaxation_rate() - self.current_Haar_num = 1 # starting with 1 - self.current_step_per_Haar = 1 - self.H_array = [] # saving all H's with Haar wavelet to be multiplied - self.H_tot = [] # Haar wavelet multipied H summed up for each time bin - self.L_array = [] # Liouvillian for each time bin - self.U_array = [] # propagation operators for each time bin - self.U = self.U_initial.copy() # multiplied propagtion operators - self.state = self.unitary_to_observation(self.U_initial) # starting observation space - self.prev_fidelity = 0 # previous step' fidelity for rewarding - self.alpha_max = 4*np.pi/self.final_time - #self.alpha_max = 0 - #self.alphaC_mod_max = 1.5E9 ## see https://journals.aps.org/prx/pdf/10.1103/PhysRevX.11.021058 - #self.alphaC_mod_max = 0.005E9 ## see https://journals.aps.org/prx/pdf/10.1103/PhysRevX.11.021058 - #self.alphaC0 = 1.0367E9 # coupler center frequency : 5.2GHz, qubit 1 center frequency: 4.16 GHz - #self.alphaC0 = 0.01E9 # coupler center frequency : 5.2GHz, qubit 1 center frequency: 4.16 GHz - self.Delta0 = 100E6 - self.Delta_mod_max = 25E6 - self.gamma_phase_max = 1.1675 * np.pi - self.gamma_magnitude_max = 1.8 * np.pi / self.final_time / self.steps_per_Haar - self.transition_history = [] - self.episode_id = 0 - - def detuning_update(self): - # Random detuning selection - if len(self.delta[0])==1: - detuning1 = self.delta[0][0] - else: - detuning1 = random.sample(self.delta[0],k=1)[0] - - # Random detuning selection - if len(self.delta[1])==1: - detuning2 = self.delta[0][0] - else: - detuning2 = random.sample(self.delta[1],k=1)[0] - - self.detuning = [detuning1, detuning2] - - - - def unitary_to_superoperator(self, U): - return (spre(Qobj(U)) * spost(Qobj(U))).data.toarray() - - def get_relaxation_rate(self): - relaxation_size = len(self.relaxation_ops) #get number of relaxation ops - - sampled_rate_list = [] - for ii in range(relaxation_size): - sampled_rate_list.append(random.sample(self.relaxation_rates_list[ii],k=1)[0]) - - return sampled_rate_list - - def get_observation(self): - normalizedDetuning = [(self.detuning[0] - min(self.delta[0])+1E-15)/(max(self.delta[0])-min(self.delta[0])+1E-15), (self.detuning[1] - min(self.delta[1])+1E-15)/(max(self.delta[1])-min(self.delta[1])+1E-15)] - return np.append([self.compute_fidelity()]+[x//6283185 for x in self.relaxation_rate]+normalizedDetuning, self.unitary_to_observation(self.U)) #6283185 assuming 500 nanosecond relaxation is max - - def compute_fidelity(self): - U_target_dagger = self.U_target.conjugate().transpose() - F = float(np.abs(np.trace(U_target_dagger @ self.U))) / (self.U.shape[0]) - return F - - def unitary_to_observation(self, U): - return ( - np.array( - [(abs(x), (cmath.phase(x) / 2 / np.pi + 1) / 2) for x in U.flatten()], - dtype=np.float64, - ) - .squeeze() - .reshape(-1) # cmath phase gives -2pi to 2pi (?) - ) - - def reset(self, *, seed=None, options=None): - self.U = self.U_initial.copy() - self.state = self.get_observation() - self.current_Haar_num = 1 - self.current_step_per_Haar = 1 - self.H_array = [] - self.H_tot = [] - self.L_array = [] - self.U_array = [] - self.prev_fidelity = 0 - self.relaxation_rate = self.get_relaxation_rate() - self.detuning = 0 - self.detuning_update() - starting_observeration = self.get_observation() - info = {} - self.episode_id += 1 - return starting_observeration, info - - def step(self, action): - num_time_bins = 2 ** (self.current_Haar_num - 1) # Haar number decides the number of time bins - - ### action space setting - alpha1 = self.alpha_max * action[0] - alpha2 = self.alpha_max * action[1] - #alphaC = self.alphaC0 + self.alphaC_mod_max * action[2] - Delta = self.Delta0 + self.Delta_mod_max * action[2] - - # gamma is the complex amplitude of the control field - gamma_magnitude1 = self.gamma_magnitude_max / 2 * (action[3] + 1) - gamma_magnitude2 = self.gamma_magnitude_max / 2 * (action[4] + 1) - - gamma_phase1 = self.gamma_phase_max * action[5] - gamma_phase2 = self.gamma_phase_max * action[6] - - # Set noise opertors - jump_ops = [] - for ii in range(len(self.relaxation_ops)): - jump_ops.append(np.sqrt(self.relaxation_rate[ii]) * self.relaxation_ops[ii]) - - # Hamiltonian with controls - H = self.hamiltonian(self.delta[0][0], self.delta[1][0], alpha1, alpha2, Delta, gamma_magnitude1, gamma_phase1, gamma_magnitude2, gamma_phase2) - self.H_array.append(H) # Array of Hs at each Haar wavelet - - # H_tot for adding Hs at each time bins - self.H_tot = [] - - for ii, H_elem in enumerate(self.H_array): - for jj in range(0, num_time_bins): - Haar_num = self.current_Haar_num - np.floor(ii / self.steps_per_Haar) # Haar_num: label which Haar wavelet, current_Haar_num: order in the array - factor = (-1) ** np.floor(jj / (2 ** (Haar_num - 1))) # factor flips the sign every 2^(Haar_num-1) - if ii > 0: - self.H_tot[jj] += factor * H_elem - else: # Because H_tot[jj] does not exist - self.H_tot.append(factor * H_elem) - - self.L = ([]) # at every step we calculate L again because minimal time bin changes - self.U = np.eye(16) # identity - - for jj in range(0, num_time_bins): - L = (liouvillian(Qobj(self.H_tot[jj]), jump_ops, data_only=False, chi=None)).data.toarray() # Liouvillian calc - self.L_array.append(L) - Ut = la.expm(self.final_time / num_time_bins * L) # time evolution (propagation operator) - self.U = Ut @ self.U # calculate total propagation until the time we are at - - # Reward and fidelity calculation - fidelity = self.compute_fidelity() - reward = (-5 * np.log10(1.0 - fidelity) + np.log10(1.0 - self.prev_fidelity)) + (5 * fidelity - self.prev_fidelity) - self.prev_fidelity = fidelity - - self.state = self.get_observation() - - - # if self.verbose is True: - # print( - # "F: ", f"{fidelity:7.3f}", - # "R: ", f"{reward:7.3f}", - # ) - - self.transition_history.append([fidelity, reward, action, self.U, self.episode_id]) - - # Determine if episode is over - truncated = False - terminated = False - if fidelity >= 1: - truncated = True # truncated when target fidelity reached - elif (self.current_Haar_num >= self.num_Haar_basis) and (self.current_step_per_Haar >= self.steps_per_Haar): # terminate when all Haar is tested - terminated = True - else: - terminated = False - - if (self.current_step_per_Haar == self.steps_per_Haar): # For each Haar basis, if all trial steps ends, them move to next haar wavelet - self.current_Haar_num += 1 - self.current_step_per_Haar = 1 - else: - self.current_step_per_Haar += 1 - - info = {} - return (self.state, reward, terminated, truncated, info) - - diff --git a/tests/relaqs/environments/test_gate_synth_env_rllib.py b/tests/relaqs/environments/test_gate_synth_env_rllib.py index 49bc3306..7d7c2be4 100644 --- a/tests/relaqs/environments/test_gate_synth_env_rllib.py +++ b/tests/relaqs/environments/test_gate_synth_env_rllib.py @@ -4,32 +4,29 @@ import sys sys.path.append('../src') from relaqs.save_results import SaveResults -from relaqs.environments.gate_synth_env_rllib_Haar import (GateSynthEnvRLlibHaarNoisy, - TwoQubitGateSynth, - GateSynthEnvRLlibHaar -) +from relaqs.environments.noisy_two_qubit_env import NoisyTwoQubitEnv +from relaqs.environments.noisy_single_qubit_env import NoisySingleQubitEnv +from relaqs.environments.single_qubit_env import SingleQubitEnv from relaqs.api.utils import (return_env_from_alg, run_noisless_one_qubit_experiment, run_noisy_one_qubit_experiment, - load_and_analyze_best_unitary ) from relaqs.api.gates import H, I, X import pandas as pd -from relaqs import RESULTS_DIR @pytest.fixture() def noisy_config(): - return GateSynthEnvRLlibHaarNoisy.get_default_env_config() + return NoisySingleQubitEnv.get_default_env_config() @pytest.fixture() def noiseless_config(): - return GateSynthEnvRLlibHaar.get_default_env_config() + return SingleQubitEnv.get_default_env_config() @pytest.fixture() def noisy_gate_environment(noisy_config): - return GateSynthEnvRLlibHaarNoisy(noisy_config) + return NoisySingleQubitEnv(noisy_config) @pytest.fixture() def number_of_training_iterations(): @@ -37,7 +34,7 @@ def number_of_training_iterations(): @pytest.fixture() def noiseless_gate_environment(noiseless_config): - return GateSynthEnvRLlibHaar(noiseless_config) + return SingleQubitEnv(noiseless_config) @pytest.fixture() def gate_to_train(request): @@ -51,7 +48,7 @@ def test_compute_fidelity_one_qubit_noisy(noisy_config): one_qubit_config["U_initial"] = I().get_matrix() one_qubit_config["U_target"] = I().get_matrix() - env = GateSynthEnvRLlibHaarNoisy(one_qubit_config) + env = NoisySingleQubitEnv(one_qubit_config) assert env.compute_fidelity() == 1.0 @@ -60,14 +57,14 @@ def test_compute_fidelity_one_qubit_noisy(noisy_config): def test_compute_fidelity_two_qubits(): - two_qubit_config = TwoQubitGateSynth.get_default_env_config() + two_qubit_config = NoisyTwoQubitEnv.get_default_env_config() I = np.array([[1, 0], [0, 1]]) II = tensor(Qobj(I),Qobj(I)).data.toarray() two_qubit_config["U_initial"] = II two_qubit_config["U_target"] = II - env = TwoQubitGateSynth(two_qubit_config) + env = NoisyTwoQubitEnv(two_qubit_config) assert env.compute_fidelity() == 1.0 @@ -138,10 +135,6 @@ def test_noiseless_training(gate_to_train, number_of_training_iterations): average_fidelity = sum(fidelities)/len(fidelities) assert average_fidelity > 0.850 -# @pytest.mark.parametrize("gate_to_train", ['x'], indirect=True) -# def test_loading_of_unitary(gate_to_train): -# data_path = RESULTS_DIR + '2023-11-08_11-09-45/env_data.csv' -# load_and_analyze_best_unitary(data_path, gate_to_train) From b49e16eb31d693eec9e1d99a091ae5d9fbf5dc97 Mon Sep 17 00:00:00 2001 From: akataba Date: Fri, 8 Mar 2024 17:06:35 -0500 Subject: [PATCH 16/31] fixing imports --- analysis/load_env_data.py | 2 +- src/relaqs/api/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/analysis/load_env_data.py b/analysis/load_env_data.py index 2136c650..7bab9073 100644 --- a/analysis/load_env_data.py +++ b/analysis/load_env_data.py @@ -3,7 +3,7 @@ """ from relaqs import RESULTS_DIR -from relaqs.api import load_pickled_env_data +from relaqs.api.utils import load_pickled_env_data data_path = RESULTS_DIR + '2024-01-24_11-37-15_X/env_data.pkl' diff --git a/src/relaqs/api/__init__.py b/src/relaqs/api/__init__.py index 8d1478f1..4e6f1310 100644 --- a/src/relaqs/api/__init__.py +++ b/src/relaqs/api/__init__.py @@ -1,4 +1,4 @@ from .training import TrainRLLib from .callbacks import GateSynthesisCallbacks from .gates import Gate -# from .utils import gate_fidelity, dm_fidelity, load_pickled_env_data + From 8f25d026364f96b73ca247697f38e89cce4cfbeb Mon Sep 17 00:00:00 2001 From: akataba Date: Tue, 12 Mar 2024 16:47:33 -0400 Subject: [PATCH 17/31] fixing environments to get tests to pass --- src/relaqs/api/utils.py | 2 +- src/relaqs/environments/single_qubit_env.py | 7 ++++++- tests/relaqs/environments/test_gate_synth_env_rllib.py | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/relaqs/api/utils.py b/src/relaqs/api/utils.py index 00b01d62..4a156426 100644 --- a/src/relaqs/api/utils.py +++ b/src/relaqs/api/utils.py @@ -204,7 +204,7 @@ def run_noisy_one_qubit_experiment(gate, n_training_iterations=1, noise_file=" # ---------------------> Configure algorithm and Environment <------------------------- alg_config = DDPGConfig() alg_config.framework("torch") - alg_config.environment("my_env", env_config=env_config) + alg_config.environment(NoisySingleQubitEnv, env_config=env_config) alg_config.rollouts(batch_mode="complete_episodes") alg_config.callbacks(GateSynthesisCallbacks) alg_config.train_batch_size = NoisySingleQubitEnv.get_default_env_config()["steps_per_Haar"] diff --git a/src/relaqs/environments/single_qubit_env.py b/src/relaqs/environments/single_qubit_env.py index e83177ac..7e434c92 100644 --- a/src/relaqs/environments/single_qubit_env.py +++ b/src/relaqs/environments/single_qubit_env.py @@ -30,6 +30,7 @@ def __init__(self, env_config): self.observation_space = gym.spaces.Box(low=-1, high=1, shape=(env_config["observation_space_size"],)) self.action_space = gym.spaces.Box(low=np.array([-1, -1, -1]), high=np.array([1, 1, 1])) self.delta = env_config["delta"] # detuning + self.detuning = 0 self.U_target = env_config["U_target"] self.U_initial = env_config["U_initial"] # future todo, can make random initial state self.U = env_config["U_initial"].copy() @@ -70,6 +71,10 @@ def compute_reward(self, fidelity): return (-3 * np.log10(1.0 - fidelity) + np.log10(1.0 - self.prev_fidelity)) + (3 * fidelity - self.prev_fidelity) def hamiltonian(self, delta, alpha, gamma_magnitude, gamma_phase): + print("delta: ", delta) + print("alpha: ", alpha) + print("gamma_magnitude: ", gamma_magnitude) + print("gamma_phase: ", gamma_phase) return (delta + alpha) * Z + gamma_magnitude * (np.cos(gamma_phase) * X + np.sin(gamma_phase) * Y) def reset(self, *, seed=None, options=None): @@ -87,7 +92,7 @@ def reset(self, *, seed=None, options=None): return starting_observeration, info def hamiltonian_update(self, alpha, gamma_magnitude, gamma_phase): - H = self.hamiltonian(self.delta, alpha, gamma_magnitude, gamma_phase) + H = self.hamiltonian(self.detuning, alpha, gamma_magnitude, gamma_phase) self.H_array.append(H) def H_tot_upate(self, num_time_bins): diff --git a/tests/relaqs/environments/test_gate_synth_env_rllib.py b/tests/relaqs/environments/test_gate_synth_env_rllib.py index 7d7c2be4..a7940de1 100644 --- a/tests/relaqs/environments/test_gate_synth_env_rllib.py +++ b/tests/relaqs/environments/test_gate_synth_env_rllib.py @@ -7,6 +7,7 @@ from relaqs.environments.noisy_two_qubit_env import NoisyTwoQubitEnv from relaqs.environments.noisy_single_qubit_env import NoisySingleQubitEnv from relaqs.environments.single_qubit_env import SingleQubitEnv +from relaqs.api.utils import run from relaqs.api.utils import (return_env_from_alg, run_noisless_one_qubit_experiment, @@ -111,6 +112,7 @@ def test_noisy_training(gate_to_train, number_of_training_iterations): n_training_iterations=n_training_iterations, noise_file=noise_file ) + env = return_env_from_alg(alg) sr = SaveResults(env, alg) save_dir = sr.save_results() @@ -125,7 +127,7 @@ def test_noiseless_training(gate_to_train, number_of_training_iterations): n_train_iterations= number_of_training_iterations alg, _ = run_noisless_one_qubit_experiment(gate_to_train, n_training_iterations=n_train_iterations - ) + ) env = return_env_from_alg(alg) sr = SaveResults(env, alg) save_dir = sr.save_results() From 8371cf2ef7a0bbe3dca24dc106ce372d3bcd1e3d Mon Sep 17 00:00:00 2001 From: akataba Date: Tue, 19 Mar 2024 11:30:48 -0400 Subject: [PATCH 18/31] reverting to changes from the main branch. This is to remove code not related to tests passing --- scripts/deterministic_agent.py | 30 +++++-------- scripts/inferencing.py | 78 ++++++++++++---------------------- 2 files changed, 37 insertions(+), 71 deletions(-) diff --git a/scripts/deterministic_agent.py b/scripts/deterministic_agent.py index e4370b82..8c1dc6a8 100644 --- a/scripts/deterministic_agent.py +++ b/scripts/deterministic_agent.py @@ -1,49 +1,41 @@ from relaqs.save_results import SaveResults import numpy as np from relaqs.api.utils import (run, - run_noisy_one_qubit_experiment, sample_noise_parameters, get_best_episode_information, return_env_from_alg ) -from relaqs.api.gates import H, X -from relaqs.plot_data import plot_results, plot_data +from relaqs.api.gates import H -n_training_iterations = 300 +n_training_iterations = 250 figure_title ="Inferencing on multiple noisy environments with different detuning noise" noise_file = "april/ibmq_belem_month_is_4.json" noise_file_2 = "april/ibmq_quito_month_is_4.json" path_to_detuning = "qubit_detuning_data.json" # --------------------------> Training of model <----------------------------------------------------- -alg, list_of_results = run_noisy_one_qubit_experiment(X(), +alg = run(H(), n_training_iterations, noise_file=noise_file ) # ----------------------- Creating the deterministic agent using actions from the best episode ------------------------------- env = return_env_from_alg(alg) -t1_list,t2_list,_ = sample_noise_parameters(noise_file_2, detuning_noise_file=path_to_detuning) -detuning_list = np.random.normal(1e8, 1e8, 9).tolist() +t1_list,t2_list, = sample_noise_parameters(noise_file_2, detuning_noise_file=path_to_detuning) +detuning_list = np.random.normal(1e8, 1e4, 9).tolist() # t2_list = np.random.normal(1e-9, 1e-5, 135) env.relaxation_rates_list = [np.reciprocal(t1_list).tolist(), np.reciprocal(t2_list).tolist()] env.delta = detuning_list -sr = SaveResults(env, alg, results=list_of_results) +sr = SaveResults(env, alg) save_dir = sr.save_results() print("Results saved to:", save_dir) -plot_data(save_dir, episode_length=alg._episode_history[0].episode_length, figure_title=figure_title) -plot_results(save_dir, figure_title=figure_title) -# best_episode_information = get_best_episode_information(save_dir + "env_data.csv") -best_episode_information = get_best_episode_information(save_dir + "env_data.pkl") +best_episode_information = get_best_episode_information(save_dir + "env_data.csv") + +actions = [np.asarray(eval(best_episode_information.iloc[0,2])), np.asarray(eval(best_episode_information.iloc[1,2]))] -print("best_episode_information actions first row: ", best_episode_information["Actions"].iloc[0]) -print("best_episode_information actions second row: ", best_episode_information["Actions"].iloc[1]) -# actions = [np.asarray(eval(best_episode_information.iloc[0,2])), np.asarray(eval(best_episode_information.iloc[1,2]))] -actions = [best_episode_information["Actions"].iloc[0], best_episode_information["Actions"].iloc[1]] -print("actions: ", actions) num_episodes = 0 episode_reward = 0.0 @@ -60,6 +52,4 @@ print(f"Episode done: Total reward = {episode_reward}") obs, info = env.reset() num_episodes += 1 - episode_reward = 0.0 - - + episode_reward = 0.0 \ No newline at end of file diff --git a/scripts/inferencing.py b/scripts/inferencing.py index f2abe177..ba7c8893 100644 --- a/scripts/inferencing.py +++ b/scripts/inferencing.py @@ -1,59 +1,35 @@ from relaqs.save_results import SaveResults -from relaqs.plot_data import plot_data, plot_results +from relaqs.plot_data import plot_data import numpy as np -from relaqs.api.utils import do_inferencing, get_best_episode_information -from relaqs.api.gates import X, H -from relaqs.api.utils import ( - run_noisy_one_qubit_experiment, - sample_noise_parameters, - return_env_from_alg -) - -best_fidelities_found = [] -for _ in range(6): - n_training_iterations = 250 - n_episodes_for_inferencing= 400 - figure_title ="Inferencing on multiple noisy environments with different detuning noise for X gate" - noise_file = "april/ibmq_belem_month_is_4.json" - noise_file_2 = "april/ibmq_quito_month_is_4.json" - path_to_detuning = "qubit_detuning_data.json" - - # -----------------------> Training model <------------------------ - alg, list_of_results = run_noisy_one_qubit_experiment(X(), - n_training_iterations, +from relaqs.api.utils import do_inferencing, run +from relaqs.api.gates import H + +noise_file = "april/ibmq_belem_month_is_4.json" +inferencing_noise_file = "april/ibmq_manila_month_is_4.json" +n_episodes_for_inferencing = 10 +save = True +plot = True +figure_title = "Inferencing with model" +n_training_iterations = 1 + +# -----------------------> Training model <------------------------ +alg = run(gate=H(), + n_training_iterations=n_training_iterations, noise_file=noise_file - ) - - # ----------------------- Creating new environment with new detuning ------------------------------- - env = return_env_from_alg(alg) - t1_list,t2_list,_ = sample_noise_parameters(noise_file_2, detuning_noise_file=path_to_detuning) - detuning_list = np.random.normal(1e8, 1e12, 9).tolist() - # t2_list = np.random.normal(1e-9, 1e-5, 135)x - env.relaxation_rates_list = [np.reciprocal(t1_list).tolist(), np.reciprocal(t2_list).tolist()] - env.delta = detuning_list - - # -----------------------> Inferencing <--------------------------- - inferencing_env, inferencing_alg = do_inferencing(alg, n_episodes_for_inferencing,quantum_noise_file_path=noise_file_2) - - # -------------------> Save Inferencing Results <--------------------------------------- - sr = SaveResults(inferencing_env, inferencing_alg) - save_dir = sr.save_results() - print("Results saved to:", save_dir) - # best_episode_information = get_best_episode_information(save_dir + "env_data.csv") - best_episode_information = get_best_episode_information(save_dir + "env_data.pkl") - - print("Fidelities from best epsiode: ", [best_episode_information.iloc[0,0], best_episode_information.iloc[1,0]]) - best_fidelities_found.append((best_episode_information.iloc[0,0],best_episode_information.iloc[1,0] )) - best_fidelity_tuple = str((best_episode_information.iloc[0,0],best_episode_information.iloc[1,0])) - best_fidelity_file = "best_fidelities.txt" - with open(save_dir + best_fidelity_file, 'w') as file: - file.write(best_fidelity_tuple) + ) +# -----------------------> Inferencing <--------------------------- +env, alg = do_inferencing(alg, n_episodes_for_inferencing,quantum_noise_file_path=inferencing_noise_file) - # ---------------------> Plot Data <------------------------------------------- - plot_data(save_dir, episode_length=inferencing_alg._episode_history[0].episode_length, figure_title=figure_title) - +# -------------------> Save Inferencing Results <--------------------------------------- +sr = SaveResults(env, alg) +save_dir = sr.save_results() +print("Results saved to:", save_dir) -print(best_fidelities_found) +# ---------------------> Plot Data <------------------------------------------- +assert save is True, "If plot=True, then save must also be set to True" +plot_data(save_dir, episode_length=alg._episode_history[0].episode_length, figure_title=figure_title) +print("Plots Created") +# -------------------------------------------------------------- From ef36ba192375d2ef4b9f964d6696d2c5149882d3 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 27 Mar 2024 11:04:46 -0400 Subject: [PATCH 19/31] removed packages that don't need to be installed during testing --- requirements.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 54b0ad30..a83ac1bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,4 @@ ray[rllib]==2.4.0 ray[tune] -matplotlib qutip -torch -tensorboard \ No newline at end of file +torch \ No newline at end of file From cfb2baf187f11046f6af458a37be2d79f0b1da19 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 13:54:10 -0400 Subject: [PATCH 20/31] respsonding to pr comments --- analysis/load_env_data.py | 2 +- scripts/best_fidelities.txt | 1 - src/relaqs/api/__init__.py | 2 +- src/relaqs/api/utils.py | 1 - .../environments/test_gate_synth_env_rllib.py | 143 ------------------ 5 files changed, 2 insertions(+), 147 deletions(-) delete mode 100644 scripts/best_fidelities.txt delete mode 100644 tests/relaqs/environments/test_gate_synth_env_rllib.py diff --git a/analysis/load_env_data.py b/analysis/load_env_data.py index 7bab9073..2136c650 100644 --- a/analysis/load_env_data.py +++ b/analysis/load_env_data.py @@ -3,7 +3,7 @@ """ from relaqs import RESULTS_DIR -from relaqs.api.utils import load_pickled_env_data +from relaqs.api import load_pickled_env_data data_path = RESULTS_DIR + '2024-01-24_11-37-15_X/env_data.pkl' diff --git a/scripts/best_fidelities.txt b/scripts/best_fidelities.txt deleted file mode 100644 index 551be50e..00000000 --- a/scripts/best_fidelities.txt +++ /dev/null @@ -1 +0,0 @@ -(0.01754923417485625, 0.9963428890697784) \ No newline at end of file diff --git a/src/relaqs/api/__init__.py b/src/relaqs/api/__init__.py index 4e6f1310..a9313b39 100644 --- a/src/relaqs/api/__init__.py +++ b/src/relaqs/api/__init__.py @@ -1,4 +1,4 @@ from .training import TrainRLLib from .callbacks import GateSynthesisCallbacks from .gates import Gate - +from .utils import gate_fidelity, dm_fidelity, load_pickled_env_data diff --git a/src/relaqs/api/utils.py b/src/relaqs/api/utils.py index 4a156426..0e4d88c6 100644 --- a/src/relaqs/api/utils.py +++ b/src/relaqs/api/utils.py @@ -5,7 +5,6 @@ from ray.rllib.algorithms.algorithm import Algorithm from ray.rllib.algorithms.ddpg import DDPGConfig from relaqs import RESULTS_DIR -import ast from ray.tune.registry import register_env from relaqs.environments.single_qubit_env import SingleQubitEnv from relaqs.environments.noisy_single_qubit_env import NoisySingleQubitEnv diff --git a/tests/relaqs/environments/test_gate_synth_env_rllib.py b/tests/relaqs/environments/test_gate_synth_env_rllib.py deleted file mode 100644 index a7940de1..00000000 --- a/tests/relaqs/environments/test_gate_synth_env_rllib.py +++ /dev/null @@ -1,143 +0,0 @@ -import numpy as np -from qutip import Qobj, tensor, cphase -import pytest -import sys -sys.path.append('../src') -from relaqs.save_results import SaveResults -from relaqs.environments.noisy_two_qubit_env import NoisyTwoQubitEnv -from relaqs.environments.noisy_single_qubit_env import NoisySingleQubitEnv -from relaqs.environments.single_qubit_env import SingleQubitEnv -from relaqs.api.utils import run - -from relaqs.api.utils import (return_env_from_alg, - run_noisless_one_qubit_experiment, - run_noisy_one_qubit_experiment, -) -from relaqs.api.gates import H, I, X -import pandas as pd - - -@pytest.fixture() -def noisy_config(): - return NoisySingleQubitEnv.get_default_env_config() - -@pytest.fixture() -def noiseless_config(): - return SingleQubitEnv.get_default_env_config() - -@pytest.fixture() -def noisy_gate_environment(noisy_config): - return NoisySingleQubitEnv(noisy_config) - -@pytest.fixture() -def number_of_training_iterations(): - return 250 - -@pytest.fixture() -def noiseless_gate_environment(noiseless_config): - return SingleQubitEnv(noiseless_config) - -@pytest.fixture() -def gate_to_train(request): - if request.param == 'x': - return X() - elif request.param == 'h': - return H() - -def test_compute_fidelity_one_qubit_noisy(noisy_config): - one_qubit_config = noisy_config - one_qubit_config["U_initial"] = I().get_matrix() - one_qubit_config["U_target"] = I().get_matrix() - - env = NoisySingleQubitEnv(one_qubit_config) - - assert env.compute_fidelity() == 1.0 - - env.U_target = env.unitary_to_superoperator(H().get_matrix()) - assert env.compute_fidelity() == 0.0 - - -def test_compute_fidelity_two_qubits(): - two_qubit_config = NoisyTwoQubitEnv.get_default_env_config() - I = np.array([[1, 0], [0, 1]]) - II = tensor(Qobj(I),Qobj(I)).data.toarray() - - two_qubit_config["U_initial"] = II - two_qubit_config["U_target"] = II - - env = NoisyTwoQubitEnv(two_qubit_config) - - assert env.compute_fidelity() == 1.0 - - CZ = cphase(np.pi).data.toarray() - env.U_target = env.unitary_to_superoperator(CZ) - assert env.compute_fidelity() == 0.25 - - - -def test_reseting_environment(noiseless_gate_environment, noiseless_config): - - # reset the environment - noiseless_gate_environment.reset() - - - # test that we initialized unitary correctly - assert np.array_equal(noiseless_gate_environment.U_initial, noiseless_config["U_initial"]) - - # for the main branch we shall make the X gate the default target gate for testing purposes - assert (X().get_matrix(), noiseless_gate_environment.U_target) - - # Previous fidelity is initially set to zero - assert 0 == noiseless_gate_environment.prev_fidelity - - # There should be initial one observation - assert 9 == len(noiseless_gate_environment.state) - - # Check that the initial flattening happened correctly - assert np.array_equal(np.array([1,0.5, 0, 0.5, 0, 0.5, 1, 0.5]), noiseless_gate_environment.state[1:9]) - -def test_unitarity(noiseless_gate_environment): - for _ in range(10): - action = noiseless_gate_environment.action_space.sample() - noiseless_gate_environment.step(action) - assert np.allclose(noiseless_gate_environment.U @ noiseless_gate_environment.U.T.conjugate(), I().get_matrix()) - -@pytest.mark.parametrize("gate_to_train", ['x'], indirect=True) -def test_noisy_training(gate_to_train, number_of_training_iterations): - - n_training_iterations = number_of_training_iterations - noise_file = "april/ibmq_belem_month_is_4.json" - - alg,_ = run_noisy_one_qubit_experiment(gate_to_train, - n_training_iterations=n_training_iterations, - noise_file=noise_file - ) - - env = return_env_from_alg(alg) - sr = SaveResults(env, alg) - save_dir = sr.save_results() - df = pd.read_csv(save_dir + "env_data.csv") - last_100_rows = df.tail(100) - fidelities = last_100_rows.iloc[:,0] - average_fidelity = sum(fidelities)/len(fidelities) - assert average_fidelity > 0.85 - -@pytest.mark.parametrize("gate_to_train", ['x', 'h'], indirect=True) -def test_noiseless_training(gate_to_train, number_of_training_iterations): - n_train_iterations= number_of_training_iterations - alg, _ = run_noisless_one_qubit_experiment(gate_to_train, - n_training_iterations=n_train_iterations - ) - env = return_env_from_alg(alg) - sr = SaveResults(env, alg) - save_dir = sr.save_results() - df = pd.read_csv(save_dir + "env_data.csv") - last_100_rows = df.tail(100) - fidelities = last_100_rows.iloc[:,0] - average_fidelity = sum(fidelities)/len(fidelities) - assert average_fidelity > 0.850 - - - - - From b2c82b94b600a1d6b5bac00ba4055caede31ac90 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 14:10:32 -0400 Subject: [PATCH 21/31] changing versions of qutip --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a83ac1bd..a5f372f1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ ray[rllib]==2.4.0 ray[tune] -qutip +qutip == 4.7.2 torch \ No newline at end of file From 705ec1198839aef36b4a77ff0d2bface7ceecb14 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 14:24:47 -0400 Subject: [PATCH 22/31] adding test folder --- tests/relaqs/environments/test_envs.py | 144 +++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 tests/relaqs/environments/test_envs.py diff --git a/tests/relaqs/environments/test_envs.py b/tests/relaqs/environments/test_envs.py new file mode 100644 index 00000000..27ffff7a --- /dev/null +++ b/tests/relaqs/environments/test_envs.py @@ -0,0 +1,144 @@ +import numpy as np +from qutip import Qobj, tensor, cphase +import pytest +import sys +sys.path.append('../src') +from relaqs.save_results import SaveResults +from relaqs.environments.noisy_two_qubit_env import NoisyTwoQubitEnv +from relaqs.environments.noisy_single_qubit_env import NoisySingleQubitEnv +from relaqs.environments.single_qubit_env import SingleQubitEnv +from relaqs.api.utils import run + +from relaqs.api.utils import (return_env_from_alg, + run_noisless_one_qubit_experiment, + run_noisy_one_qubit_experiment, + run +) +from relaqs.api.gates import H, I, X +import pandas as pd + + +@pytest.fixture() +def noisy_config(): + return NoisySingleQubitEnv.get_default_env_config() + +@pytest.fixture() +def noiseless_config(): + return SingleQubitEnv.get_default_env_config() + +@pytest.fixture() +def noisy_gate_environment(noisy_config): + return NoisySingleQubitEnv(noisy_config) + +@pytest.fixture() +def number_of_training_iterations(): + return 250 + +@pytest.fixture() +def noiseless_gate_environment(noiseless_config): + return SingleQubitEnv(noiseless_config) + +@pytest.fixture() +def gate_to_train(request): + if request.param == 'x': + return X() + elif request.param == 'h': + return H() + +def test_compute_fidelity_one_qubit_noisy(noisy_config): + one_qubit_config = noisy_config + one_qubit_config["U_initial"] = I().get_matrix() + one_qubit_config["U_target"] = I().get_matrix() + + env = NoisySingleQubitEnv(one_qubit_config) + + assert env.compute_fidelity() == 1.0 + + env.U_target = env.unitary_to_superoperator(H().get_matrix()) + assert env.compute_fidelity() == 0.0 + + +def test_compute_fidelity_two_qubits(): + two_qubit_config = NoisyTwoQubitEnv.get_default_env_config() + I = np.array([[1, 0], [0, 1]]) + II = tensor(Qobj(I),Qobj(I)).data.toarray() + + two_qubit_config["U_initial"] = II + two_qubit_config["U_target"] = II + + env = NoisyTwoQubitEnv(two_qubit_config) + + assert env.compute_fidelity() == 1.0 + + CZ = cphase(np.pi).data.toarray() + env.U_target = env.unitary_to_superoperator(CZ) + assert env.compute_fidelity() == 0.25 + + + +def test_reseting_environment(noiseless_gate_environment, noiseless_config): + + # reset the environment + noiseless_gate_environment.reset() + + + # test that we initialized unitary correctly + assert np.array_equal(noiseless_gate_environment.U_initial, noiseless_config["U_initial"]) + + # for the main branch we shall make the X gate the default target gate for testing purposes + assert (X().get_matrix(), noiseless_gate_environment.U_target) + + # Previous fidelity is initially set to zero + assert 0 == noiseless_gate_environment.prev_fidelity + + # There should be initial one observation + assert 9 == len(noiseless_gate_environment.state) + + # Check that the initial flattening happened correctly + assert np.array_equal(np.array([1,0.5, 0, 0.5, 0, 0.5, 1, 0.5]), noiseless_gate_environment.state[1:9]) + +def test_unitarity(noiseless_gate_environment): + for _ in range(10): + action = noiseless_gate_environment.action_space.sample() + noiseless_gate_environment.step(action) + assert np.allclose(noiseless_gate_environment.U @ noiseless_gate_environment.U.T.conjugate(), I().get_matrix()) + +@pytest.mark.parametrize("gate_to_train", ['x'], indirect=True) +def test_noisy_training(gate_to_train, number_of_training_iterations): + + n_training_iterations = number_of_training_iterations + noise_file = "april/ibmq_belem_month_is_4.json" + + alg,_ = run_noisy_one_qubit_experiment(gate_to_train, + n_training_iterations=n_training_iterations, + noise_file=noise_file + ) + + env = return_env_from_alg(alg) + sr = SaveResults(env, alg) + save_dir = sr.save_results() + df = pd.read_csv(save_dir + "env_data.csv") + last_100_rows = df.tail(100) + fidelities = last_100_rows.iloc[:,0] + average_fidelity = sum(fidelities)/len(fidelities) + assert average_fidelity > 0.85 + +@pytest.mark.parametrize("gate_to_train", ['x', 'h'], indirect=True) +def test_noiseless_training(gate_to_train, number_of_training_iterations): + n_train_iterations= number_of_training_iterations + alg, _ = run_noisless_one_qubit_experiment(gate_to_train, + n_training_iterations=n_train_iterations + ) + env = return_env_from_alg(alg) + sr = SaveResults(env, alg) + save_dir = sr.save_results() + df = pd.read_csv(save_dir + "env_data.csv") + last_100_rows = df.tail(100) + fidelities = last_100_rows.iloc[:,0] + average_fidelity = sum(fidelities)/len(fidelities) + assert average_fidelity > 0.850 + + + + + From 19ff62da87c1e2e5ede286e41b1ac534967d71c3 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 15:54:02 -0400 Subject: [PATCH 23/31] dealing with circuilar imports --- analysis/load_env_data.py | 3 ++- src/relaqs/api/__init__.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/analysis/load_env_data.py b/analysis/load_env_data.py index 2136c650..a17f3612 100644 --- a/analysis/load_env_data.py +++ b/analysis/load_env_data.py @@ -3,7 +3,8 @@ """ from relaqs import RESULTS_DIR -from relaqs.api import load_pickled_env_data +# from relaqs.api import load_pickled_env_data +from relaqs.api.utils load_pickled_env_data data_path = RESULTS_DIR + '2024-01-24_11-37-15_X/env_data.pkl' diff --git a/src/relaqs/api/__init__.py b/src/relaqs/api/__init__.py index a9313b39..8d1478f1 100644 --- a/src/relaqs/api/__init__.py +++ b/src/relaqs/api/__init__.py @@ -1,4 +1,4 @@ from .training import TrainRLLib from .callbacks import GateSynthesisCallbacks from .gates import Gate -from .utils import gate_fidelity, dm_fidelity, load_pickled_env_data +# from .utils import gate_fidelity, dm_fidelity, load_pickled_env_data From 0f56beab76328eb5eb8be1d177252407da8b5915 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 15:59:01 -0400 Subject: [PATCH 24/31] changing version of qutip --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a5f372f1..830a6b4e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ ray[rllib]==2.4.0 ray[tune] -qutip == 4.7.2 +qutip torch \ No newline at end of file From 154af649b7fc2e9de345ddb3af6aea030767079a Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 16:02:13 -0400 Subject: [PATCH 25/31] changing version of qutip --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 830a6b4e..62d16ce9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ ray[rllib]==2.4.0 ray[tune] -qutip +qutip==4.7.0 torch \ No newline at end of file From 744938f7457ba2cde19efaf762197cee67aa4891 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 16:08:40 -0400 Subject: [PATCH 26/31] changing version of qutip --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 62d16ce9..301af4ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ ray[rllib]==2.4.0 ray[tune] -qutip==4.7.0 +qutip==4.7.5 torch \ No newline at end of file From 5824ae529aa464dd58970d02fe3302a3acd49276 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 16:40:38 -0400 Subject: [PATCH 27/31] changing version of qutip --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 301af4ac..a83ac1bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ ray[rllib]==2.4.0 ray[tune] -qutip==4.7.5 +qutip torch \ No newline at end of file From 1ecfffbc525dcd3d186e096252992c1c8794d2f3 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 16:46:17 -0400 Subject: [PATCH 28/31] changing version of qutip --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index a83ac1bd..ad14c3bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ ray[rllib]==2.4.0 ray[tune] qutip -torch \ No newline at end of file +torch +numpy==1.19 \ No newline at end of file From c327517d6e25b0e286261b9f9efe45d62d535af8 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 17:50:37 -0400 Subject: [PATCH 29/31] changing version of qutip --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index ad14c3bd..a97e057d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -ray[rllib]==2.4.0 +ray[rllib]==2.5.0 ray[tune] -qutip +qutip==4.7.2 torch -numpy==1.19 \ No newline at end of file +git+https://github.com/ray-project/ray.git#subdirectory=rllib_contrib/ddpg From a39697fd40ff02848bf9fdd5977a23a5873a1542 Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 18:20:28 -0400 Subject: [PATCH 30/31] changing version of qutip --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index a97e057d..c1f0434f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ ray[tune] qutip==4.7.2 torch git+https://github.com/ray-project/ray.git#subdirectory=rllib_contrib/ddpg +scipy==1.9.1 From 9d126bd26a9a9cc0f69ea106a29d95938701c02c Mon Sep 17 00:00:00 2001 From: akataba Date: Wed, 17 Apr 2024 18:37:21 -0400 Subject: [PATCH 31/31] changing to new version of ddpg --- src/relaqs/api/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/relaqs/api/utils.py b/src/relaqs/api/utils.py index 0e4d88c6..95bbde94 100644 --- a/src/relaqs/api/utils.py +++ b/src/relaqs/api/utils.py @@ -3,7 +3,8 @@ import pandas as pd from scipy.linalg import sqrtm from ray.rllib.algorithms.algorithm import Algorithm -from ray.rllib.algorithms.ddpg import DDPGConfig +# from ray.rllib.algorithms.ddpg import DDPGConfig +from rllib_ddpg.ddpg import DDPGConfig from relaqs import RESULTS_DIR from ray.tune.registry import register_env from relaqs.environments.single_qubit_env import SingleQubitEnv