├── deploy ├── sac_cnn.zip ├── sac_mlp.zip └── augment.py ├── testing ├── sac_mlp_large.zip ├── sac_cnn_20_20_150.zip ├── qaoa_noisy.py ├── qaoa.py ├── vqe_noisy.py ├── vqe.py ├── circles_noisy.py ├── moons_noisy.py ├── regression.py ├── moons.py ├── circles.py └── blobs.py ├── README.md ├── training ├── mlp_train.py ├── cnn_train.py └── env.py ├── examples └── pennylane_bp.py └── LICENSE /deploy/sac_cnn.zip: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8d5a620b448ee324b7d2c39473af95003a38f0fbf9e9d9ebf5f35145cca39281 3 | size 57173825 4 | -------------------------------------------------------------------------------- /deploy/sac_mlp.zip: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:78761dc256b6603c032b31efec5011b70d5e6e61e1e2f043ac0ca9ffba8cf308 3 | size 186206966 4 | -------------------------------------------------------------------------------- /testing/sac_mlp_large.zip: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:78761dc256b6603c032b31efec5011b70d5e6e61e1e2f043ac0ca9ffba8cf308 3 | size 186206966 4 | -------------------------------------------------------------------------------- /testing/sac_cnn_20_20_150.zip: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8d5a620b448ee324b7d2c39473af95003a38f0fbf9e9d9ebf5f35145cca39281 3 | size 57173825 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Unitary Fund](https://img.shields.io/badge/Supported%20By-UNITARY%20FUND-brightgreen.svg?style=for-the-badge)](http://unitary.fund) 2 | 3 | 4 | # Reinforcement Learning for Quantum Variational Circuit Optimization 5 | Code for [Optimizing Quantum Variational Circuits with Deep Reinforcement Learning](https://arxiv.org/abs/2109.03188) 6 | 7 | To use the pretrained models, don't worry about training or testing. Download deploy folder and use the `mixed` function in `augment.py` to work with your circuit. See the file for the required inputs. Note that deployment usage requires stable_baselines3 (which requires PyTorch) and numpy. 8 | 9 | To recreate the results from the paper, use the testing folder. Each file runs and outputs the information as presented in the table. Note that this has dependencies on TensorFlow, TensorFlow-Quantum, sklearn, and stable_baselines3 (which requires PyTorch). 10 | 11 | To train your own agent, go to the training folder. Specifiy the maximum sizes and training durations for the agent and run the code. 12 | 13 | # Examples for Deployment 14 | 15 | The example (written in pennylane) is similar to that used to generate the barren plateaus figures in the paper. 16 | 17 | # Questions? 18 | 19 | Join the rl_opt channel in the Unitary Fund discord. 20 | -------------------------------------------------------------------------------- /training/mlp_train.py: -------------------------------------------------------------------------------- 1 | from env import RandomCircuits 2 | import sys 3 | import numpy as np 4 | from stable_baselines3 import TD3 5 | from stable_baselines3 import SAC 6 | from stable_baselines3 import PPO 7 | from stable_baselines3.common.noise import NormalActionNoise 8 | from stable_baselines3.common.callbacks import CheckpointCallback 9 | 10 | steps = 5000000 11 | windows = 50 12 | learn_delay = 10000 13 | buffer = 100000 14 | load = False 15 | 16 | # Max Qubits, Max Depth, Max Optimization Steps, Learner, t 17 | 18 | q = int(sys.argv[1]) 19 | d = int(sys.argv[2]) 20 | opt = int(sys.argv[3]) 21 | env = RandomCircuits(q, d, opt) 22 | if sys.argv[5] == 't': 23 | load = True 24 | 25 | if sys.argv[4] == "t": 26 | if load: 27 | agent = TD3.load("td3", env=env) 28 | print("loaded") 29 | else: 30 | policy_kwargs = dict(net_arch=dict(pi=[1028, 512], qf=[1028, 512])) 31 | n_actions = env.action_space.shape[-1] 32 | action_noise = NormalActionNoise(mean=np.zeros(n_actions), sigma=0.1 * np.ones(n_actions)) 33 | agent = TD3("MlpPolicy", env, action_noise=action_noise, verbose=1, buffer_size=buffer, policy_kwargs=policy_kwargs, learning_starts=learn_delay) 34 | elif sys.argv[4] == 's': 35 | if load: 36 | agent = SAC.load("sac", env=env) 37 | print("loaded") 38 | else: 39 | policy_kwargs = dict(net_arch=dict(pi=[1028, 512], qf=[1028, 512])) 40 | agent = SAC("MlpPolicy", env, verbose=1, buffer_size=buffer, policy_kwargs=policy_kwargs, learning_starts=learn_delay) 41 | elif sys.argv[4] == 'p': 42 | if load: 43 | agent = PPO.load("ppo", env=env) 44 | print("loaded") 45 | else: 46 | policy_kwargs = dict(net_arch=[dict(pi=[1028, 512], vf=[1028, 512])]) 47 | agent = PPO("MlpPolicy", env, verbose=1, policy_kwargs=policy_kwargs) 48 | 49 | checkpoint_callback = CheckpointCallback(save_freq=50000, save_path='./', name_prefix=sys.argv[4]) 50 | agent.learn(total_timesteps=steps, log_interval=10, callback=checkpoint_callback) 51 | -------------------------------------------------------------------------------- /deploy/augment.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from stable_baselines3 import SAC 3 | 4 | sac_mlp_agent = SAC.load("sac_mlp") 5 | sac_cnn_agent = SAC.load("sac_cnn") 6 | 7 | def mlp_encoding(size, num_q, num_d, struct, weights, error, input_type): 8 | state = np.zeros(shape=(size, 8)) 9 | for i in range(len(weights)): 10 | q = i % num_q 11 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, input_type] 12 | return state.flatten() 13 | 14 | def cnn_encoding(max_q, max_d, num_q, struct, weights, error, input_type): 15 | state = np.zeros(shape=(max_q, max_d, 5)) 16 | for i in range(len(weights)): 17 | qubit_number = i % num_q 18 | depth_number = i // num_q 19 | state[qubit_number][depth_number][struct[i]] = weights[i] 20 | state[:,:,3] = input_type 21 | state[:,:,4] = error 22 | return state.transpose(2, 0, 1) 23 | 24 | 25 | ''' 26 | Inputs: 27 | f = function that takes weights as input and returns the loss 28 | structure = 1D list of integers that represent the structure (0 = RX, 1 = RY, 2 = RZ) starting a qubit 0 depth 0 and going down each qubit for every depth 29 | number_of_qubits = number of qubits in the system 30 | current_params = the current weights of the circuit 31 | current_loss = the current loss of the model 32 | input_type = 0 for |0> state and 1 for equal superposition state 33 | ind = whether or not to use a single [ind]ividual example 34 | ''' 35 | def mixed(f, structure, number_of_qubits, current_params, current_loss, input_type, ind=False): 36 | number_of_params = len(structure) 37 | mlp_enc = mlp_encoding(400, number_of_qubits, len(structure) // number_of_qubits, structure, current_params, current_loss, input_type) 38 | cnn_enc = cnn_encoding(20, 20, number_of_qubits, structure, current_params, current_loss, input_type) 39 | mlp_weights = np.array([sac_mlp_agent.predict(mlp_enc)[0][:number_of_params],]) 40 | cnn_weights = np.array([sac_cnn_agent.predict(cnn_enc)[0][:number_of_params],]) 41 | if ind: 42 | mlp_loss = f(mlp_weights[0]) 43 | cnn_loss = f(cnn_weights[0]) 44 | if mlp_loss < cnn_loss: 45 | return mlp_weights[0] 46 | else: 47 | return cnn_weights[0] 48 | else: 49 | mlp_loss = f(mlp_weights) 50 | cnn_loss = f(cnn_weights) 51 | if mlp_loss < cnn_loss: 52 | return mlp_weights 53 | else: 54 | return cnn_weights 55 | -------------------------------------------------------------------------------- /examples/pennylane_bp.py: -------------------------------------------------------------------------------- 1 | import pennylane as qml 2 | from pennylane import numpy as np 3 | import matplotlib.pyplot as plt 4 | from augment import mixed 5 | 6 | # Code from: https://pennylane.ai/qml/demos/tutorial_local_cost_functions.html 7 | 8 | wires = 6 9 | dev = qml.device("default.qubit", wires=wires, shots=10000) 10 | 11 | 12 | def global_cost_simple(rotations): 13 | for i in range(wires): 14 | qml.RX(rotations[i], wires=i) 15 | for i in range(wires): 16 | qml.RY(rotations[wires + i], wires=i) 17 | return qml.probs(wires=range(wires)) 18 | 19 | 20 | global_circuit = qml.QNode(global_cost_simple, dev) 21 | 22 | def cost_global(rotations): 23 | return 1 - global_circuit(rotations)[0] 24 | 25 | 26 | rotations = np.array([[3.] * len(range(wires)), [0.] * len(range(wires))], requires_grad=True).flatten() 27 | #rotations = np.random.uniform(-1, 1, 12) 28 | params_aug = np.array([[3.] * len(range(wires)), [0.] * len(range(wires))], requires_grad=True).flatten() 29 | params_aug1 = params_aug.copy() 30 | opt = qml.GradientDescentOptimizer(stepsize=0.2) 31 | steps = 50 32 | params_global = rotations 33 | 34 | gds = [] 35 | augs = [] 36 | # No augmentation 37 | for i in range(steps): 38 | # update the circuit parameters 39 | current_loss = cost_global(params_global) 40 | param_old = params_aug 41 | current_aug = cost_global(param_old) 42 | params_global = opt.step(cost_global, params_global) 43 | params_aug1 = mixed(cost_global, [0] * 6 + [1] * 6, 6, params_aug1, current_loss, 0, True) 44 | temp_gd = np.array([i for i in params_aug1], requires_grad=True) 45 | temp_gd1 = np.array([i for i in param_old], requires_grad=True) 46 | temp_gd = opt.step(cost_global, temp_gd) 47 | temp_gd1 = opt.step(cost_global, temp_gd1) 48 | aug_l = cost_global(params_aug1) 49 | gd_l = cost_global(temp_gd) 50 | gd_l1 = cost_global(temp_gd1) 51 | x = min([aug_l, gd_l, gd_l1]) 52 | y = np.argmin([aug_l, gd_l, gd_l1]) 53 | if x < current_aug: 54 | if y == 0: 55 | params_aug = params_aug1 56 | elif y == 1: 57 | params_aug = temp_gd 58 | elif y == 2: 59 | params_aug = temp_gd1 60 | else: 61 | params_aug = param_old 62 | 63 | 64 | gd_l = cost_global(params_global) 65 | aug_l = cost_global(params_aug) 66 | gds.append(gd_l) 67 | augs.append(aug_l) 68 | if (i + 1) % 5 == 0: 69 | print("GD Cost after step {:5d}: {: .7f}".format(i + 1, gd_l)) 70 | print("Aug Cost after step {:5d}: {: .7f}".format(i + 1, aug_l)) 71 | #if cost_global(params_global) < 0.1: 72 | # break 73 | 74 | print(qml.draw(global_circuit)(params_global)) 75 | print(qml.draw(global_circuit)(params_aug)) 76 | 77 | 78 | plt.plot(gds, label='Gradient Descent') 79 | plt.plot(augs, label='Augmented (Rolling Average)') 80 | plt.legend() 81 | plt.ylabel('Loss') 82 | plt.show() 83 | -------------------------------------------------------------------------------- /training/cnn_train.py: -------------------------------------------------------------------------------- 1 | from env import RandomCircuits 2 | import sys 3 | import numpy as np 4 | from stable_baselines3 import TD3 5 | from stable_baselines3 import SAC 6 | from stable_baselines3 import PPO 7 | from stable_baselines3.common.noise import NormalActionNoise 8 | from stable_baselines3.common.callbacks import CheckpointCallback 9 | from stable_baselines3.common.torch_layers import BaseFeaturesExtractor 10 | import torch as th 11 | import torch.nn as nn 12 | import gym 13 | 14 | class CustomCNN(BaseFeaturesExtractor): 15 | """ 16 | :param observation_space: (gym.Space) 17 | :param features_dim: (int) Number of features extracted. 18 | This corresponds to the number of unit for the last layer. 19 | """ 20 | 21 | def __init__(self, observation_space: gym.spaces.Box, features_dim: int = 256): 22 | super(CustomCNN, self).__init__(observation_space, features_dim) 23 | # We assume CxHxW images (channels Last) 24 | n_input_channels = observation_space.shape[0] 25 | self.cnn = nn.Sequential( 26 | nn.Conv2d(n_input_channels, 32, kernel_size=2, stride=2, padding=0), 27 | nn.ReLU(), 28 | nn.Conv2d(32, 64, kernel_size=2, stride=1, padding=0), 29 | nn.ReLU(), 30 | nn.Flatten(), 31 | ) 32 | # Compute shape by doing one forward pass 33 | with th.no_grad(): 34 | n_flatten = self.cnn( 35 | th.as_tensor(observation_space.sample()[None]).float() 36 | ).shape[1] 37 | 38 | self.linear = nn.Sequential(nn.Linear(n_flatten, features_dim), nn.ReLU()) 39 | 40 | def forward(self, observations: th.Tensor) -> th.Tensor: 41 | return self.linear(self.cnn(observations)) 42 | 43 | policy_kwargs = dict( 44 | features_extractor_class=CustomCNN, 45 | features_extractor_kwargs=dict(features_dim=512), 46 | ) 47 | 48 | steps = 5000000 49 | learn_delay = 10000 50 | buffer = 100000 51 | load = False 52 | 53 | # Max Qubits, Max Depth, Max Optimization Steps, Learner, Load 54 | 55 | q = int(sys.argv[1]) 56 | d = int(sys.argv[2]) 57 | opt = int(sys.argv[3]) 58 | env = RandomCircuits(q, d, opt, "block") 59 | #print(env.observation_space.shape, env.action_space.shape) 60 | if sys.argv[5] == 't': 61 | load = True 62 | 63 | if sys.argv[4] == "t": 64 | if load: 65 | agent = TD3.load("tcnn", env=env) 66 | print("loaded") 67 | else: 68 | n_actions = env.action_space.shape[-1] 69 | action_noise = NormalActionNoise(mean=np.zeros(n_actions), sigma=0.1 * np.ones(n_actions)) 70 | agent = TD3("CnnPolicy", env, action_noise=action_noise, verbose=1, buffer_size=buffer, policy_kwargs=policy_kwargs, learning_starts=learn_delay) 71 | elif sys.argv[4] == 's': 72 | if load: 73 | agent = SAC.load("scnn", env=env) 74 | print("loaded") 75 | else: 76 | agent = SAC("CnnPolicy", env, verbose=1, buffer_size=buffer, policy_kwargs=policy_kwargs, learning_starts=learn_delay) 77 | elif sys.argv[4] == 'p': 78 | if load: 79 | agent = PPO.load("pcnn", env=env) 80 | print("loaded") 81 | else: 82 | agent = PPO("CnnPolicy", env, verbose=1, policy_kwargs=policy_kwargs) 83 | 84 | checkpoint_callback = CheckpointCallback(save_freq=50000, save_path='./', name_prefix=sys.argv[4]+"cnn") 85 | agent.learn(total_timesteps=steps, log_interval=10, callback=checkpoint_callback) 86 | -------------------------------------------------------------------------------- /training/env.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import tensorflow_quantum as tfq 3 | import cirq 4 | import random 5 | import sympy 6 | import numpy as np 7 | import operator 8 | from functools import reduce 9 | import gym 10 | from gym import spaces 11 | 12 | def prod(iterable): 13 | return reduce(operator.mul, iterable, 1) 14 | 15 | class RandomCircuits(gym.Env): 16 | metadata = {'render.modes': ['human']} 17 | def __init__(self, max_qubits, max_depth, optimization_steps, enc_type='feature') -> None: 18 | super(RandomCircuits, self).__init__() 19 | self.max_qubits = max_qubits 20 | self.max_depth = max_depth 21 | self.max_symbols = max_depth * max_qubits 22 | self.symbols = sympy.symbols('q0:%d'%self.max_symbols) 23 | self.cost_functions = [self.vqe_cost, self.first_cost, self.sum_cost] 24 | self.struct, self.rend, self.circuit, self.target, self.input_circuit, self.ins = self.create_circuit() 25 | self.steps = 0 26 | self.max_steps = optimization_steps 27 | self.num_params = None 28 | self.num_q = None 29 | self.num_d = None 30 | self.action_space = spaces.Box(0, 2 * np.pi, (self.max_symbols,), dtype=np.float32) 31 | if enc_type == 'feature': 32 | self.observation_space = spaces.Box(-np.inf, np.inf, (self.max_symbols * 8,), dtype=np.float32) 33 | elif enc_type == 'block': 34 | self.observation_space = spaces.Box(-np.inf, np.inf, (5, self.max_qubits, self.max_depth), dtype=np.float32) 35 | self.info = dict() 36 | self.enc_type = enc_type 37 | 38 | def vqe_cost(self, qubits): 39 | return prod([cirq.Z(qubits[i]) for i in range(len(qubits))]), np.random.uniform(-1, 1, 1) 40 | 41 | def first_cost(self, qubits): 42 | return [cirq.Z(qubits[0])], np.random.uniform(-1, 1, 1) 43 | 44 | def sum_cost(self, qubits): 45 | return sum([cirq.Z(qubits[i]) for i in range(len(qubits))]), np.random.uniform(-len(qubits), len(qubits), 1) 46 | 47 | # struct is qubit first, by depth 48 | def create_circuit(self): 49 | qubits = [cirq.GridQubit(0, i) for i in range(random.randint(2, self.max_qubits))] 50 | depth = random.randint(1, self.max_depth) 51 | circuit = cirq.Circuit() 52 | struct = [] 53 | for d in range(depth): 54 | for i in range(len(qubits)): 55 | circuit += cirq.CNOT(qubits[i], qubits[(i + 1) % len(qubits)]) 56 | for i, qubit in enumerate(qubits): 57 | random_n = np.random.uniform() 58 | if random_n > 2. / 3.: 59 | circuit += cirq.rz(self.symbols[d * len(qubits) + i])(qubit) 60 | struct.append(2) 61 | elif random_n > 1. / 3.: 62 | circuit += cirq.ry(self.symbols[d * len(qubits) + i])(qubit) 63 | struct.append(1) 64 | else: 65 | circuit += cirq.rx(self.symbols[d * len(qubits) + i])(qubit) 66 | struct.append(0) 67 | self.num_q = len(qubits) 68 | self.num_d = depth 69 | self.num_params = depth * len(qubits) 70 | cost_fn = random.choice(self.cost_functions) 71 | readout_ops, target_val = cost_fn(qubits) 72 | ins = tf.keras.layers.Input(shape=(), dtype=tf.dtypes.string) 73 | init = tf.keras.initializers.RandomUniform(0, 2 * np.pi) 74 | layer1 = tfq.layers.PQC(circuit, readout_ops, repetitions=1000, initializer=init)(ins) 75 | model = tf.keras.models.Model(inputs=ins, outputs=layer1) 76 | if np.random.uniform() > 0.5: 77 | cir_in = tfq.convert_to_tensor([cirq.Circuit()]) 78 | cir_ins = None 79 | else: 80 | cir_in = tfq.convert_to_tensor([cirq.Circuit([cirq.H(i) for i in qubits])]) 81 | cir_ins = "H" 82 | return struct, circuit, model, target_val, cir_in, cir_ins 83 | 84 | def get_error(self): 85 | return -abs(self.circuit(self.input_circuit).numpy()[0][0] - self.target[0])**2 86 | 87 | # Return State, Reward, Done 88 | def step(self, action): 89 | done = False 90 | if self.steps > self.max_steps: 91 | done = True 92 | #old = self.circuit(self.input_circuit) 93 | #print(action) 94 | self.circuit.set_weights([action[:self.num_params]]) 95 | error = self.get_error() 96 | if -error < 0.001: 97 | done = True 98 | self.steps += 1 99 | observation = self.encoding(action[:self.num_params], error) 100 | return observation, error, done, self.info 101 | 102 | # FLIP inspired 103 | # max_params X 8 array 104 | # each param = [current error, current value, gate type, qubit number, qubit layer, max_qubits, max_depth, input] 105 | # Feature = https://arxiv.org/pdf/2103.07585.pdf inspired 106 | # [qubit, depth, num_gate_classes (3) + inputs (1) + error (1)] 107 | # num_gate_classes = 3 (Rx, Ry, Rz) 108 | def encoding(self, weights, error): 109 | if self.enc_type == 'feature': 110 | state = np.zeros(shape=(self.max_symbols, 8)) 111 | for i in range(len(weights)): 112 | q = i % self.num_q 113 | #print(weights[i], self.struct[i], q, i//self.max_qubits, self.max_qubits, self.max_depth, 0 if self.ins is None else 1) 114 | state[i] = [error, weights[i], self.struct[i], q, i//self.num_q, self.num_q, self.num_d, 0 if self.ins is None else 1] 115 | return state.flatten() 116 | elif self.enc_type == 'block': 117 | state = np.zeros(shape=(self.max_qubits, self.max_depth, 5)) 118 | for i in range(len(weights)): 119 | qubit_number = i % self.num_q 120 | depth_number = i // self.num_q 121 | state[qubit_number][depth_number][self.struct[i]] = weights[i] 122 | state[:,:,3] = 0 if self.ins is None else 1 123 | state[:,:,4] = error 124 | return state.transpose(2, 0, 1) 125 | 126 | def reset(self): 127 | self.struct, self.rend, self.circuit, self.target, self.input_circuit, self.ins = self.create_circuit() 128 | self.steps = 0 129 | return self.encoding(self.circuit.trainable_variables[0].numpy(), self.get_error()) 130 | 131 | def render(self, mode='human'): 132 | print(self.rend) 133 | 134 | ''' 135 | if __name__ == "__main__": 136 | env = RandomCircuits(20, 20, 150, enc_type='feature') 137 | #print(env.observation_space, env.max_symbols) 138 | state = env.reset() 139 | iterations = 1 140 | for i in range(iterations): 141 | done = False 142 | env.render() 143 | while not done: 144 | state, reward, done, _ = env.step(np.random.uniform(0, np.pi, env.max_symbols)) 145 | print(state, reward, done) 146 | for i in range(5): 147 | print("SHEET") 148 | print(state[:,:,i]) 149 | ''' 150 | ''' 151 | from random_circuit_env import RandomCircuits 152 | env = RandomCircuits(10, 10, 100, 100) 153 | from stable_baselines3.common.env_checker import check_env 154 | check_env(env) 155 | ''' 156 | -------------------------------------------------------------------------------- /testing/qaoa_noisy.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import tensorflow_quantum as tfq 3 | import cirq 4 | import sympy 5 | import numpy as np 6 | import networkx as nx 7 | from stable_baselines3 import SAC 8 | 9 | def to_dec(x): 10 | return int("".join(str(i) for i in x), 2) 11 | 12 | nodes = 5 13 | regularity = 2 14 | maxcut_graph = nx.random_regular_graph(n=nodes, d=regularity) 15 | 16 | def mixing_hamiltonian(c, qubits, par): 17 | for i in range(len(qubits)): 18 | c += cirq.rx(2 * par).on(qubits[i]) 19 | return c 20 | 21 | def cost_hamiltonian(c, qubits, g, ps): 22 | for edge in g.edges(): 23 | c += cirq.CNOT(qubits[edge[0]], qubits[edge[1]]) 24 | c += cirq.rz(ps).on(qubits[edge[1]]) 25 | c += cirq.CNOT(qubits[edge[0]], qubits[edge[1]]) 26 | return c 27 | 28 | qs = [cirq.GridQubit(0, i) for i in range(nodes)] 29 | qaoa_circuit = cirq.Circuit() 30 | p = 10 31 | 32 | num_param = 2 * p 33 | qaoa_parameters = sympy.symbols("q0:%d"%num_param) 34 | for i in range(p): 35 | qaoa_circuit = cost_hamiltonian(qaoa_circuit, qs, maxcut_graph, qaoa_parameters[2 * i]) 36 | qaoa_circuit = mixing_hamiltonian(qaoa_circuit, qs, qaoa_parameters[2 * i + 1]) 37 | 38 | initial = cirq.Circuit() 39 | for i in qs: 40 | initial.append(cirq.H(i)) 41 | 42 | c_inputs = tfq.convert_to_tensor([initial]) 43 | 44 | def cc(qubits, g): 45 | c = 0 46 | for edge in g.edges(): 47 | c += cirq.PauliString(1/2 * cirq.Z(qubits[edge[0]]) * cirq.Z(qubits[edge[1]])) 48 | return c 49 | 50 | def loss_fn(x): 51 | return x.numpy() 52 | 53 | cost = cc(qs, maxcut_graph) 54 | opt = tf.keras.optimizers.Adam(lr=0.01) 55 | es = 150 56 | 57 | def encoding(size, num_q, num_d, struct, weights, error): 58 | state = np.zeros(shape=(size, 8)) 59 | for i in range(len(weights)): 60 | q = i % num_q 61 | #print(weights[i], self.struct[i], q, i//self.max_qubits, self.max_qubits, self.max_depth, 0 if self.ins is None else 1) 62 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, 1] 63 | return state.flatten() 64 | 65 | def cnn_enc(max_q, max_d, num_q, struct, weights, error): 66 | state = np.zeros(shape=(max_q, max_d, 5)) 67 | for i in range(len(weights)): 68 | qubit_number = i % num_q 69 | depth_number = i // num_q 70 | state[qubit_number][depth_number][struct[i]] = weights[i] 71 | state[:,:,3] = 1 72 | state[:,:,4] = error 73 | return state.transpose(2, 0, 1) 74 | 75 | sac_agent = SAC.load("sac_mlp_large") 76 | sac_cnn_agent = SAC.load("sac_cnn_20_20_150") 77 | opter = tf.keras.optimizers.Adam(lr=0.01) 78 | 79 | mlp_mins_train = [] 80 | cnn_mins_train = [] 81 | grad_mins_train = [] 82 | mixed_mins_train = [] 83 | 84 | rep = 3 85 | for it in range(rep): 86 | print(it) 87 | ins = tf.keras.layers.Input(shape=(), dtype=tf.dtypes.string) 88 | outs = tfq.layers.NoisyPQC(qaoa_circuit, cost, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(ins) 89 | vqc = tf.keras.models.Model(inputs=ins, outputs=outs) 90 | 91 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 92 | layer1 = tfq.layers.NoisyPQC(qaoa_circuit, cost, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 93 | sac_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 94 | 95 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 96 | layer1 = tfq.layers.NoisyPQC(qaoa_circuit, cost, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 97 | sac_cnn_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 98 | 99 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 100 | layer1 = tfq.layers.NoisyPQC(qaoa_circuit, cost, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 101 | mixed = tf.keras.models.Model(inputs=inputs, outputs=layer1) 102 | 103 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 104 | layer1 = tfq.layers.NoisyPQC(qaoa_circuit, cost, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 105 | mixed_test = tf.keras.models.Model(inputs=inputs, outputs=layer1) 106 | 107 | mixed_test.set_weights(vqc.get_weights()) 108 | sac_vqc.set_weights(vqc.get_weights()) 109 | sac_cnn_vqc.set_weights(vqc.get_weights()) 110 | mixed.set_weights(vqc.get_weights()) 111 | 112 | sac_loss = [] 113 | sac_cnn_loss = [] 114 | mixed_loss = [] 115 | 116 | history = [] 117 | 118 | for i in range(es): 119 | with tf.GradientTape() as tape: 120 | error = vqc(c_inputs) 121 | grads = tape.gradient(error, vqc.trainable_variables) 122 | opt.apply_gradients(zip(grads, vqc.trainable_variables)) 123 | history.append(error.numpy()) 124 | print(i, history[-1]) 125 | 126 | for i in range(es): 127 | print(i, es) 128 | 129 | # SAC 130 | sac_error = loss_fn(sac_vqc(c_inputs)) 131 | sac_enc = encoding(400, nodes, 2, ([2 for _ in range(nodes)] + [0 for _ in range(nodes)]) * p, sac_vqc.trainable_variables[0].numpy(), sac_error) 132 | action, _ = sac_agent.predict(sac_enc) 133 | sac_vqc.set_weights([action[:num_param]]) 134 | 135 | # SAC_CNN 136 | sac_cnn_error = loss_fn(sac_cnn_vqc(c_inputs)) 137 | sac_cnn_enc = cnn_enc(20, 20, nodes, ([2 for _ in range(nodes)] + [0 for _ in range(nodes)]) * p, sac_cnn_vqc.trainable_variables[0].numpy(), sac_cnn_error) 138 | action, _ = sac_cnn_agent.predict(sac_cnn_enc) 139 | sac_cnn_vqc.set_weights([action[:num_param]]) 140 | 141 | with tf.GradientTape() as tape1: 142 | loss = mixed(c_inputs) 143 | grads = tape1.gradient(loss, mixed.trainable_variables) 144 | opter.apply_gradients(zip(grads, mixed.trainable_variables)) 145 | 146 | sac_loss.append(loss_fn(sac_vqc(c_inputs))) 147 | sac_cnn_loss.append(loss_fn(sac_cnn_vqc(c_inputs))) 148 | 149 | sac_enc = encoding(400, nodes, 2, ([2 for _ in range(nodes)] + [0 for _ in range(nodes)]) * p, sac_vqc.trainable_variables[0].numpy(), loss.numpy()) 150 | mlp_action, _ = sac_agent.predict(sac_enc) 151 | mixed_test.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 152 | mlp_loss = loss_fn(mixed_test(c_inputs)) 153 | sac_cnn_enc = cnn_enc(20, 20, nodes, ([2 for _ in range(nodes)] + [0 for _ in range(nodes)]) * p, sac_cnn_vqc.trainable_variables[0].numpy(), loss.numpy()) 154 | cnn_action, _ = sac_cnn_agent.predict(sac_cnn_enc) 155 | mixed_test.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 156 | cnn_loss = loss_fn(mixed_test(c_inputs)) 157 | 158 | losses = [mlp_loss, cnn_loss, loss_fn(mixed(c_inputs))] 159 | best = losses.index(min(losses)) 160 | if best == 0: 161 | mixed.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 162 | elif best == 1: 163 | mixed.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 164 | 165 | mixed_loss.append(loss_fn(mixed(c_inputs))) 166 | 167 | cnn_mins_train.append(min(sac_cnn_loss)) 168 | mlp_mins_train.append(min(sac_loss)) 169 | mixed_mins_train.append(min(mixed_loss)) 170 | grad_mins_train.append(min(history)) 171 | 172 | print("Training") 173 | print("SAC", np.mean(mlp_mins_train), np.std(mlp_mins_train), "SAC CNN", np.mean(cnn_mins_train), np.std(cnn_mins_train), "Gradient",\ 174 | np.mean(grad_mins_train), np.std(grad_mins_train), "Mixed", np.mean(mixed_mins_train), np.std(mixed_mins_train)) 175 | -------------------------------------------------------------------------------- /testing/qaoa.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import tensorflow_quantum as tfq 3 | import cirq 4 | import sympy 5 | import numpy as np 6 | import networkx as nx 7 | from stable_baselines3 import SAC 8 | 9 | def to_dec(x): 10 | return int("".join(str(i) for i in x), 2) 11 | 12 | nodes = 20 13 | regularity = 2 14 | maxcut_graph = nx.random_regular_graph(n=nodes, d=regularity) 15 | 16 | def mixing_hamiltonian(c, qubits, par): 17 | for i in range(len(qubits)): 18 | c += cirq.rx(2 * par).on(qubits[i]) 19 | return c 20 | 21 | def cost_hamiltonian(c, qubits, g, ps): 22 | for edge in g.edges(): 23 | c += cirq.CNOT(qubits[edge[0]], qubits[edge[1]]) 24 | c += cirq.rz(ps).on(qubits[edge[1]]) 25 | c += cirq.CNOT(qubits[edge[0]], qubits[edge[1]]) 26 | return c 27 | 28 | qs = [cirq.GridQubit(0, i) for i in range(nodes)] 29 | qaoa_circuit = cirq.Circuit() 30 | p = 10 31 | 32 | num_param = 2 * p 33 | qaoa_parameters = sympy.symbols("q0:%d"%num_param) 34 | for i in range(p): 35 | qaoa_circuit = cost_hamiltonian(qaoa_circuit, qs, maxcut_graph, qaoa_parameters[2 * i]) 36 | qaoa_circuit = mixing_hamiltonian(qaoa_circuit, qs, qaoa_parameters[2 * i + 1]) 37 | 38 | initial = cirq.Circuit() 39 | for i in qs: 40 | initial.append(cirq.H(i)) 41 | 42 | c_inputs = tfq.convert_to_tensor([initial]) 43 | 44 | def cc(qubits, g): 45 | c = 0 46 | for edge in g.edges(): 47 | c += cirq.PauliString(1/2 * cirq.Z(qubits[edge[0]]) * cirq.Z(qubits[edge[1]])) 48 | return c 49 | 50 | def loss_fn(x): 51 | return x.numpy() 52 | 53 | cost = cc(qs, maxcut_graph) 54 | opt = tf.keras.optimizers.Adam(lr=0.01) 55 | es = 150 56 | 57 | def encoding(size, num_q, num_d, struct, weights, error): 58 | state = np.zeros(shape=(size, 8)) 59 | for i in range(len(weights)): 60 | q = i % num_q 61 | #print(weights[i], self.struct[i], q, i//self.max_qubits, self.max_qubits, self.max_depth, 0 if self.ins is None else 1) 62 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, 1] 63 | return state.flatten() 64 | 65 | def cnn_enc(max_q, max_d, num_q, struct, weights, error): 66 | state = np.zeros(shape=(max_q, max_d, 5)) 67 | for i in range(len(weights)): 68 | qubit_number = i % num_q 69 | depth_number = i // num_q 70 | state[qubit_number][depth_number][struct[i]] = weights[i] 71 | state[:,:,3] = 1 72 | state[:,:,4] = error 73 | return state.transpose(2, 0, 1) 74 | 75 | sac_agent = SAC.load("sac_mlp_large") 76 | sac_cnn_agent = SAC.load("sac_cnn_20_20_150") 77 | opter = tf.keras.optimizers.Adam(lr=0.01) 78 | 79 | mlp_mins_train = [] 80 | cnn_mins_train = [] 81 | grad_mins_train = [] 82 | mixed_mins_train = [] 83 | 84 | rep = 3 85 | for it in range(rep): 86 | print(it) 87 | inputs = tf.keras.layers.Input(shape=(), dtype=tf.dtypes.string) 88 | #layer1 = tfq.layers.PQC(qaoa_circuit, cost, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(ins) 89 | layer1 = tfq.layers.PQC(qaoa_circuit, cost, differentiator=tfq.differentiators.Adjoint())(inputs) 90 | vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 91 | 92 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 93 | #layer1 = tfq.layers.PQC(qaoa_circuit, cost, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 94 | layer1 = tfq.layers.PQC(qaoa_circuit, cost, differentiator=tfq.differentiators.Adjoint())(inputs) 95 | sac_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 96 | 97 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 98 | #layer1 = tfq.layers.PQC(qaoa_circuit, cost, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 99 | layer1 = tfq.layers.PQC(qaoa_circuit, cost, differentiator=tfq.differentiators.Adjoint())(inputs) 100 | sac_cnn_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 101 | 102 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 103 | #layer1 = tfq.layers.PQC(qaoa_circuit, cost, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 104 | layer1 = tfq.layers.PQC(qaoa_circuit, cost, differentiator=tfq.differentiators.Adjoint())(inputs) 105 | mixed = tf.keras.models.Model(inputs=inputs, outputs=layer1) 106 | 107 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 108 | #layer1 = tfq.layers.PQC(qaoa_circuit, cost, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 109 | layer1 = tfq.layers.PQC(qaoa_circuit, cost, differentiator=tfq.differentiators.Adjoint())(inputs) 110 | mixed_test = tf.keras.models.Model(inputs=inputs, outputs=layer1) 111 | 112 | mixed_test.set_weights(vqc.get_weights()) 113 | sac_vqc.set_weights(vqc.get_weights()) 114 | sac_cnn_vqc.set_weights(vqc.get_weights()) 115 | mixed.set_weights(vqc.get_weights()) 116 | 117 | sac_loss = [] 118 | sac_cnn_loss = [] 119 | mixed_loss = [] 120 | 121 | history = [] 122 | 123 | for i in range(es): 124 | with tf.GradientTape() as tape: 125 | error = vqc(c_inputs) 126 | grads = tape.gradient(error, vqc.trainable_variables) 127 | opt.apply_gradients(zip(grads, vqc.trainable_variables)) 128 | history.append(error.numpy()) 129 | print(i, history[-1]) 130 | 131 | for i in range(es): 132 | print(i, es) 133 | 134 | # SAC 135 | sac_error = loss_fn(sac_vqc(c_inputs)) 136 | sac_enc = encoding(400, nodes, 2, ([2 for _ in range(nodes)] + [0 for _ in range(nodes)]) * p, sac_vqc.trainable_variables[0].numpy(), sac_error) 137 | action, _ = sac_agent.predict(sac_enc) 138 | sac_vqc.set_weights([action[:num_param]]) 139 | 140 | # SAC_CNN 141 | sac_cnn_error = loss_fn(sac_cnn_vqc(c_inputs)) 142 | sac_cnn_enc = cnn_enc(20, 20, nodes, ([2 for _ in range(nodes)] + [0 for _ in range(nodes)]) * p, sac_cnn_vqc.trainable_variables[0].numpy(), sac_cnn_error) 143 | action, _ = sac_cnn_agent.predict(sac_cnn_enc) 144 | sac_cnn_vqc.set_weights([action[:num_param]]) 145 | 146 | with tf.GradientTape() as tape1: 147 | loss = mixed(c_inputs) 148 | grads = tape1.gradient(loss, mixed.trainable_variables) 149 | opter.apply_gradients(zip(grads, mixed.trainable_variables)) 150 | 151 | sac_loss.append(loss_fn(sac_vqc(c_inputs))) 152 | sac_cnn_loss.append(loss_fn(sac_cnn_vqc(c_inputs))) 153 | 154 | sac_enc = encoding(400, nodes, 2, ([2 for _ in range(nodes)] + [0 for _ in range(nodes)]) * p, sac_vqc.trainable_variables[0].numpy(), loss.numpy()) 155 | mlp_action, _ = sac_agent.predict(sac_enc) 156 | mixed_test.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 157 | mlp_loss = loss_fn(mixed_test(c_inputs)) 158 | sac_cnn_enc = cnn_enc(20, 20, nodes, ([2 for _ in range(nodes)] + [0 for _ in range(nodes)]) * p, sac_cnn_vqc.trainable_variables[0].numpy(), loss.numpy()) 159 | cnn_action, _ = sac_cnn_agent.predict(sac_cnn_enc) 160 | mixed_test.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 161 | cnn_loss = loss_fn(mixed_test(c_inputs)) 162 | 163 | losses = [mlp_loss, cnn_loss, loss_fn(mixed(c_inputs))] 164 | best = losses.index(min(losses)) 165 | if best == 0: 166 | mixed.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 167 | elif best == 1: 168 | mixed.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 169 | 170 | mixed_loss.append(loss_fn(mixed(c_inputs))) 171 | 172 | cnn_mins_train.append(min(sac_cnn_loss)) 173 | mlp_mins_train.append(min(sac_loss)) 174 | mixed_mins_train.append(min(mixed_loss)) 175 | grad_mins_train.append(min(history)) 176 | 177 | print("Training") 178 | print("$", np.mean(mlp_mins_train), "\pm", np.std(mlp_mins_train), "$ & $", np.mean(cnn_mins_train), "\pm", np.std(cnn_mins_train), "$ & $",\ 179 | np.mean(grad_mins_train), "\pm", np.std(grad_mins_train), "$ & $", np.mean(mixed_mins_train), "\pm", np.std(mixed_mins_train), "$") 180 | -------------------------------------------------------------------------------- /testing/vqe_noisy.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import tensorflow_quantum as tfq 3 | import cirq 4 | import sympy 5 | import numpy as np 6 | import random 7 | from functools import reduce 8 | import operator 9 | from stable_baselines3 import SAC 10 | 11 | tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR) 12 | 13 | def layer(circuit, qubits, parameters): 14 | for i in range(len(qubits)): 15 | circuit.append([cirq.rx(parameters[i]).on(qubits[i])]) 16 | for i in range(len(qubits)): 17 | circuit.append([cirq.rz(parameters[len(qubits) + i]).on(qubits[i])]) 18 | for i in range(len(qubits)-1): 19 | circuit.append([cirq.CNOT(qubits[i], qubits[i+1])]) 20 | return circuit 21 | 22 | def ansatz(circuit, qubits, layers, parameters): 23 | for i in range(layers): 24 | p = parameters[2 * i * len(qubits):2 * (i + 1) * len(qubits)] 25 | circuit = layer(circuit, qubits, p) 26 | return circuit 27 | 28 | def hamiltonian(circuit, qubits, ham): 29 | for i in range(len(qubits)): 30 | if ham[i] == "x": 31 | circuit.append(cirq.ry(-np.pi/2).on(qubits[i])) 32 | elif ham[i] == "y": 33 | circuit.append(cirq.rx(np.pi/2).on(qubits[i])) 34 | return circuit 35 | 36 | def create_vqe(qubits, layers, parameters, ham): 37 | circuit = ansatz(cirq.Circuit(), qubits, layers, parameters) 38 | circuit += hamiltonian(circuit, qubits, ham) 39 | circuit = circuit.with_noise(cirq.depolarize(p=0.075)) 40 | return circuit 41 | 42 | def prod(iterable): 43 | return reduce(operator.mul, iterable, 1) 44 | 45 | def expcost(qubits, ham): 46 | return prod([cirq.Z(qubits[i]) for i in range(len(qubits)) if ham[i] != "i"]) 47 | 48 | possibilities = ["i", "x", "y", "z"] 49 | l = 10 50 | q = 5 51 | 52 | hamilton = [[random.choice(possibilities) for _ in range(q)] for _ in range(l)] 53 | h_weights = [random.uniform(-1, 1) for _ in range(l)] 54 | 55 | lay = 5 56 | 57 | qubits = [cirq.GridQubit(0, i) for i in range(q)] 58 | num_param = lay * 2 * q 59 | params = sympy.symbols('vqe0:%d'%num_param) 60 | 61 | class VQE(tf.keras.layers.Layer): 62 | def __init__(self, num_weights, circuits, ops) -> None: 63 | super(VQE, self).__init__() 64 | self.w = tf.Variable(np.random.uniform(0, np.pi, (1, num_weights)), dtype=tf.float32) 65 | self.layers = [tfq.layers.NoisyControlledPQC(circuits[i], ops[i], repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift()) \ 66 | for i in range(len(circuits))] 67 | 68 | def call(self, input): 69 | return sum([self.layers[i]([input, self.w]) for i in range(len(self.layers))]) 70 | 71 | c_inputs = tfq.convert_to_tensor([cirq.Circuit()]) 72 | vqe_components = [] 73 | cs = [] 74 | op = [] 75 | 76 | for i in range(len(hamilton)): 77 | readout_ops = h_weights[i] * expcost(qubits, hamilton[i]) 78 | op.append(readout_ops) 79 | cs.append(create_vqe(qubits, lay, params, hamilton[i])) 80 | 81 | opt = tf.keras.optimizers.Adam(lr=0.01) 82 | 83 | def loss_fn(x): 84 | return x.numpy() 85 | 86 | es = 150 87 | 88 | def encoding(size, num_q, num_d, struct, weights, error): 89 | state = np.zeros(shape=(size, 8)) 90 | for i in range(len(weights)): 91 | q = i % num_q 92 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, 0] 93 | return state.flatten() 94 | 95 | def cnn_enc(max_q, max_d, num_q, struct, weights, error): 96 | state = np.zeros(shape=(max_q, max_d, 5)) 97 | for i in range(len(weights)): 98 | qubit_number = i % num_q 99 | depth_number = i // num_q 100 | state[qubit_number][depth_number][struct[i]] = weights[i] 101 | state[:,:,3] = 0 102 | state[:,:,4] = error 103 | return state.transpose(2, 0, 1) 104 | 105 | sac_agent = SAC.load("sac_mlp_large") 106 | sac_cnn_agent = SAC.load("sac_cnn_20_20_150") 107 | opter = tf.keras.optimizers.Adam(lr=0.01) 108 | 109 | mlp_mins_train = [] 110 | cnn_mins_train = [] 111 | grad_mins_train = [] 112 | mixed_mins_train = [] 113 | 114 | rep = 3 115 | for it in range(rep): 116 | print(it) 117 | ins = tf.keras.layers.Input(shape=(), dtype=tf.dtypes.string) 118 | v = VQE(num_param, cs, op)(ins) 119 | vqc = tf.keras.models.Model(inputs=ins, outputs=v) 120 | 121 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 122 | layer1 = VQE(num_param, cs, op)(inputs) 123 | sac_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 124 | 125 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 126 | layer1 = VQE(num_param, cs, op)(inputs) 127 | sac_cnn_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 128 | 129 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 130 | layer1 = VQE(num_param, cs, op)(inputs) 131 | mixed = tf.keras.models.Model(inputs=inputs, outputs=layer1) 132 | 133 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 134 | layer1 = VQE(num_param, cs, op)(inputs) 135 | mixed_test = tf.keras.models.Model(inputs=inputs, outputs=layer1) 136 | 137 | sac_vqc.set_weights(vqc.get_weights()) 138 | sac_cnn_vqc.set_weights(vqc.get_weights()) 139 | mixed.set_weights(vqc.get_weights()) 140 | 141 | history = [] 142 | sac_loss = [] 143 | sac_cnn_loss = [] 144 | mixed_loss = [] 145 | 146 | for i in range(es): 147 | print(i, es) 148 | 149 | # SAC 150 | sac_error = loss_fn(sac_vqc(c_inputs)) 151 | sac_enc = encoding(400, q, lay * 2, ([0 for _ in range(q)] + [2 for _ in range(q)]) * lay, sac_vqc.trainable_variables[0].numpy()[0], sac_error) 152 | action, _ = sac_agent.predict(sac_enc) 153 | sac_vqc.set_weights([np.array([action[:num_param],])]) 154 | 155 | # SAC_CNN 156 | sac_cnn_error = loss_fn(sac_cnn_vqc(c_inputs)) 157 | sac_cnn_enc = cnn_enc(20, 20, q, ([0 for _ in range(q)] + [2 for _ in range(q)]) * lay, sac_cnn_vqc.trainable_variables[0].numpy()[0], sac_cnn_error) 158 | action, _ = sac_cnn_agent.predict(sac_cnn_enc) 159 | sac_cnn_vqc.set_weights([np.array([action[:num_param],])]) 160 | 161 | 162 | with tf.GradientTape() as tape: 163 | loss = mixed(c_inputs) 164 | grads = tape.gradient(loss, mixed.trainable_variables) 165 | opter.apply_gradients(zip(grads, mixed.trainable_variables)) 166 | 167 | sac_loss.append(loss_fn(sac_vqc(c_inputs))) 168 | sac_cnn_loss.append(loss_fn(sac_cnn_vqc(c_inputs))) 169 | 170 | sac_enc = encoding(400, q, lay * 2, ([0 for _ in range(q)] + [2 for _ in range(q)]) * lay, sac_vqc.trainable_variables[0].numpy()[0], loss.numpy()) 171 | mlp_action, _ = sac_agent.predict(sac_enc) 172 | mixed_test.set_weights([np.array([mlp_action[:num_param],])]) 173 | mlp_loss = loss_fn(mixed_test(c_inputs)) 174 | sac_cnn_enc = cnn_enc(20, 20, q, ([0 for _ in range(q)] + [2 for _ in range(q)]) * lay, sac_cnn_vqc.trainable_variables[0].numpy()[0], loss.numpy()) 175 | cnn_action, _ = sac_cnn_agent.predict(sac_cnn_enc) 176 | mixed_test.set_weights([np.array([cnn_action[:num_param],])]) 177 | cnn_loss = loss_fn(mixed_test(c_inputs)) 178 | 179 | losses = [mlp_loss, cnn_loss, loss_fn(mixed(c_inputs))] 180 | best = losses.index(min(losses)) 181 | if best == 0: 182 | mixed.set_weights([np.array([mlp_action[:num_param],])]) 183 | elif best == 1: 184 | mixed.set_weights([np.array([cnn_action[:num_param],])]) 185 | mixed_loss.append(loss_fn(mixed(c_inputs))) 186 | 187 | for i in range(es): 188 | with tf.GradientTape() as tape: 189 | error = vqc(c_inputs) 190 | grads = tape.gradient(error, vqc.trainable_variables) 191 | opt.apply_gradients(zip(grads, vqc.trainable_variables)) 192 | history.append(error.numpy()) 193 | print(i, history[-1]) 194 | 195 | cnn_mins_train.append(min(sac_cnn_loss)) 196 | mlp_mins_train.append(min(sac_loss)) 197 | mixed_mins_train.append(min(mixed_loss)) 198 | grad_mins_train.append(min(history)) 199 | 200 | print("Training") 201 | print("$", np.mean(mlp_mins_train), "\pm", np.std(mlp_mins_train), "$ & $", np.mean(cnn_mins_train), "\pm", np.std(cnn_mins_train), "$ & $",\ 202 | np.mean(grad_mins_train), "\pm", np.std(grad_mins_train), "$ & $", np.mean(mixed_mins_train), "\pm", np.std(mixed_mins_train), "$") 203 | -------------------------------------------------------------------------------- /testing/vqe.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import tensorflow_quantum as tfq 3 | import cirq 4 | import sympy 5 | import numpy as np 6 | import random 7 | from functools import reduce 8 | import operator 9 | from stable_baselines3 import SAC 10 | 11 | tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR) 12 | 13 | def layer(circuit, qubits, parameters): 14 | for i in range(len(qubits)): 15 | circuit.append([cirq.rx(parameters[i]).on(qubits[i])]) 16 | for i in range(len(qubits)): 17 | circuit.append([cirq.rz(parameters[len(qubits) + i]).on(qubits[i])]) 18 | for i in range(len(qubits)-1): 19 | circuit.append([cirq.CNOT(qubits[i], qubits[i+1])]) 20 | return circuit 21 | 22 | def ansatz(circuit, qubits, layers, parameters): 23 | for i in range(layers): 24 | p = parameters[2 * i * len(qubits):2 * (i + 1) * len(qubits)] 25 | circuit = layer(circuit, qubits, p) 26 | return circuit 27 | 28 | def hamiltonian(circuit, qubits, ham): 29 | for i in range(len(qubits)): 30 | if ham[i] == "x": 31 | circuit.append(cirq.ry(-np.pi/2).on(qubits[i])) 32 | elif ham[i] == "y": 33 | circuit.append(cirq.rx(np.pi/2).on(qubits[i])) 34 | return circuit 35 | 36 | def create_vqe(qubits, layers, parameters, ham): 37 | circuit = ansatz(cirq.Circuit(), qubits, layers, parameters) 38 | circuit += hamiltonian(circuit, qubits, ham) 39 | return circuit 40 | 41 | def prod(iterable): 42 | return reduce(operator.mul, iterable, 1) 43 | 44 | def expcost(qubits, ham): 45 | return prod([cirq.Z(qubits[i]) for i in range(len(qubits)) if ham[i] != "i"]) 46 | 47 | possibilities = ["i", "x", "y", "z"] 48 | l = 10 49 | q = 20 50 | 51 | hamilton = [[random.choice(possibilities) for _ in range(q)] for _ in range(l)] 52 | h_weights = [random.uniform(-1, 1) for _ in range(l)] 53 | 54 | lay = 5 55 | 56 | qubits = [cirq.GridQubit(0, i) for i in range(q)] 57 | num_param = lay * 2 * q 58 | params = sympy.symbols('vqe0:%d'%num_param) 59 | 60 | class VQE(tf.keras.layers.Layer): 61 | def __init__(self, num_weights, circuits, ops) -> None: 62 | super(VQE, self).__init__() 63 | self.w = tf.Variable(np.random.uniform(0, np.pi, (1, num_weights)), dtype=tf.float32) 64 | #self.layers = [tfq.layers.ControlledPQC(circuits[i], ops[i], repetitions=1000, differentiator=tfq.differentiators.ParameterShift()) for i in range(len(circuits))] 65 | self.layers = [tfq.layers.ControlledPQC(circuits[i], ops[i], differentiator=tfq.differentiators.Adjoint()) for i in range(len(circuits))] 66 | 67 | 68 | def call(self, input): 69 | return sum([self.layers[i]([input, self.w]) for i in range(len(self.layers))]) 70 | 71 | c_inputs = tfq.convert_to_tensor([cirq.Circuit()]) 72 | vqe_components = [] 73 | cs = [] 74 | op = [] 75 | 76 | for i in range(len(hamilton)): 77 | readout_ops = h_weights[i] * expcost(qubits, hamilton[i]) 78 | op.append(readout_ops) 79 | cs.append(create_vqe(qubits, lay, params, hamilton[i])) 80 | 81 | opt = tf.keras.optimizers.Adam(lr=0.01) 82 | 83 | def loss_fn(x): 84 | return x.numpy() 85 | 86 | es = 150 87 | 88 | def encoding(size, num_q, num_d, struct, weights, error): 89 | state = np.zeros(shape=(size, 8)) 90 | for i in range(len(weights)): 91 | q = i % num_q 92 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, 0] 93 | return state.flatten() 94 | 95 | def cnn_enc(max_q, max_d, num_q, struct, weights, error): 96 | state = np.zeros(shape=(max_q, max_d, 5)) 97 | for i in range(len(weights)): 98 | qubit_number = i % num_q 99 | depth_number = i // num_q 100 | state[qubit_number][depth_number][struct[i]] = weights[i] 101 | state[:,:,3] = 0 102 | state[:,:,4] = error 103 | return state.transpose(2, 0, 1) 104 | 105 | sac_agent = SAC.load("sac_mlp_large") 106 | sac_cnn_agent = SAC.load("sac_cnn_20_20_150") 107 | opter = tf.keras.optimizers.Adam(lr=0.01) 108 | 109 | mlp_mins_train = [] 110 | cnn_mins_train = [] 111 | grad_mins_train = [] 112 | mixed_mins_train = [] 113 | 114 | rep = 3 115 | for it in range(rep): 116 | print(it) 117 | ins = tf.keras.layers.Input(shape=(), dtype=tf.dtypes.string) 118 | v = VQE(num_param, cs, op)(ins) 119 | vqc = tf.keras.models.Model(inputs=ins, outputs=v) 120 | 121 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 122 | layer1 = VQE(num_param, cs, op)(inputs) 123 | sac_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 124 | 125 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 126 | layer1 = VQE(num_param, cs, op)(inputs) 127 | sac_cnn_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 128 | 129 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 130 | layer1 = VQE(num_param, cs, op)(inputs) 131 | mixed = tf.keras.models.Model(inputs=inputs, outputs=layer1) 132 | 133 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 134 | layer1 = VQE(num_param, cs, op)(inputs) 135 | mixed_test = tf.keras.models.Model(inputs=inputs, outputs=layer1) 136 | 137 | sac_vqc.set_weights(vqc.get_weights()) 138 | sac_cnn_vqc.set_weights(vqc.get_weights()) 139 | mixed.set_weights(vqc.get_weights()) 140 | 141 | history = [] 142 | sac_loss = [] 143 | sac_cnn_loss = [] 144 | mixed_loss = [] 145 | 146 | for i in range(es): 147 | print(i, es) 148 | 149 | # SAC 150 | sac_error = loss_fn(sac_vqc(c_inputs)) 151 | sac_enc = encoding(400, q, lay * 2, ([0 for _ in range(q)] + [2 for _ in range(q)]) * lay, sac_vqc.trainable_variables[0].numpy()[0], sac_error) 152 | action, _ = sac_agent.predict(sac_enc) 153 | sac_vqc.set_weights([np.array([action[:num_param],])]) 154 | 155 | # SAC_CNN 156 | sac_cnn_error = loss_fn(sac_cnn_vqc(c_inputs)) 157 | sac_cnn_enc = cnn_enc(20, 20, q, ([0 for _ in range(q)] + [2 for _ in range(q)]) * lay, sac_cnn_vqc.trainable_variables[0].numpy()[0], sac_cnn_error) 158 | action, _ = sac_cnn_agent.predict(sac_cnn_enc) 159 | sac_cnn_vqc.set_weights([np.array([action[:num_param],])]) 160 | 161 | 162 | with tf.GradientTape() as tape: 163 | loss = mixed(c_inputs) 164 | grads = tape.gradient(loss, mixed.trainable_variables) 165 | opter.apply_gradients(zip(grads, mixed.trainable_variables)) 166 | 167 | sac_loss.append(loss_fn(sac_vqc(c_inputs))) 168 | sac_cnn_loss.append(loss_fn(sac_cnn_vqc(c_inputs))) 169 | 170 | sac_enc = encoding(400, q, lay * 2, ([0 for _ in range(q)] + [2 for _ in range(q)]) * lay, sac_vqc.trainable_variables[0].numpy()[0], loss.numpy()) 171 | mlp_action, _ = sac_agent.predict(sac_enc) 172 | mixed_test.set_weights([np.array([mlp_action[:num_param],])]) 173 | mlp_loss = loss_fn(mixed_test(c_inputs)) 174 | sac_cnn_enc = cnn_enc(20, 20, q, ([0 for _ in range(q)] + [2 for _ in range(q)]) * lay, sac_cnn_vqc.trainable_variables[0].numpy()[0], loss.numpy()) 175 | cnn_action, _ = sac_cnn_agent.predict(sac_cnn_enc) 176 | mixed_test.set_weights([np.array([cnn_action[:num_param],])]) 177 | cnn_loss = loss_fn(mixed_test(c_inputs)) 178 | 179 | losses = [mlp_loss, cnn_loss, loss_fn(mixed(c_inputs))] 180 | best = losses.index(min(losses)) 181 | if best == 0: 182 | mixed.set_weights([np.array([mlp_action[:num_param],])]) 183 | elif best == 1: 184 | mixed.set_weights([np.array([cnn_action[:num_param],])]) 185 | mixed_loss.append(loss_fn(mixed(c_inputs))) 186 | 187 | for i in range(es): 188 | with tf.GradientTape() as tape: 189 | error = vqc(c_inputs) 190 | grads = tape.gradient(error, vqc.trainable_variables) 191 | opt.apply_gradients(zip(grads, vqc.trainable_variables)) 192 | history.append(error.numpy()) 193 | print(i, history[-1]) 194 | 195 | cnn_mins_train.append(min(sac_cnn_loss)) 196 | mlp_mins_train.append(min(sac_loss)) 197 | mixed_mins_train.append(min(mixed_loss)) 198 | grad_mins_train.append(min(history)) 199 | 200 | print("Training") 201 | print("$", np.mean(mlp_mins_train), "\pm", np.std(mlp_mins_train), "$ & $", np.mean(cnn_mins_train), "\pm", np.std(cnn_mins_train), "$ & $",\ 202 | np.mean(grad_mins_train), "\pm", np.std(grad_mins_train), "$ & $", np.mean(mixed_mins_train), "\pm", np.std(mixed_mins_train), "$") 203 | -------------------------------------------------------------------------------- /testing/circles_noisy.py: -------------------------------------------------------------------------------- 1 | import tensorflow_quantum as tfq 2 | import tensorflow as tf 3 | import cirq 4 | import sympy 5 | import numpy as np 6 | from sklearn import datasets as ds 7 | from sklearn.model_selection import train_test_split 8 | from sklearn.preprocessing import MinMaxScaler 9 | from stable_baselines3 import SAC 10 | 11 | circle_data, circle_labels = ds.make_circles(300, noise=0.2, factor=0.3, shuffle=True) 12 | circle_data = MinMaxScaler().fit_transform(circle_data) 13 | 14 | def convert_data(data, qubits, test=False): 15 | cs = [] 16 | for i in data: 17 | cir = cirq.Circuit() 18 | cir += cirq.rx(i[0] * np.pi).on(qubits[0]) 19 | cir += cirq.rz(i[0] * np.pi).on(qubits[0]) 20 | cir += cirq.rx(i[1] * np.pi).on(qubits[1]) 21 | cir += cirq.rz(i[1] * np.pi).on(qubits[1]) 22 | cs.append(cir) 23 | if test: 24 | return tfq.convert_to_tensor([cs]) 25 | return tfq.convert_to_tensor(cs) 26 | 27 | def encode(data, labels, qubits): 28 | X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=.2, random_state=43) 29 | return convert_data(X_train, qubits), convert_data(X_test, qubits), y_train, y_test 30 | 31 | def layer(circuit, qubits, params): 32 | circuit += cirq.CNOT(qubits[0], qubits[1]) 33 | circuit += cirq.ry(params[0]).on(qubits[0]) 34 | circuit += cirq.ry(params[1]).on(qubits[1]) 35 | circuit += cirq.rz(params[2]).on(qubits[0]) 36 | circuit += cirq.rz(params[3]).on(qubits[1]) 37 | return circuit 38 | 39 | def model_circuit(qubits, depth): 40 | cir = cirq.Circuit() 41 | num_params = depth * 4 42 | params = sympy.symbols("q0:%d"%num_params) 43 | for i in range(depth): 44 | cir = layer(cir, qubits, params[i * 4:i * 4 + 4]) 45 | return cir 46 | 47 | qs = [cirq.GridQubit(0, i) for i in range(2)] 48 | d = 6 49 | X_train, X_test, y_train, y_test = encode(circle_data, circle_labels, qs) 50 | c = model_circuit(qs, d) 51 | c = c.with_noise(cirq.depolarize(p=0.075)) 52 | readout_operators = [cirq.Z(qs[0])] 53 | 54 | es = 150 55 | bs = len(X_train) 56 | 57 | def encoding(size, num_q, num_d, struct, weights, error): 58 | state = np.zeros(shape=(size, 8)) 59 | for i in range(len(weights)): 60 | q = i % num_q 61 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, 0] 62 | return state.flatten() 63 | 64 | def cnn_enc(max_q, max_d, num_q, struct, weights, error): 65 | state = np.zeros(shape=(max_q, max_d, 5)) 66 | for i in range(len(weights)): 67 | qubit_number = i % num_q 68 | depth_number = i // num_q 69 | state[qubit_number][depth_number][struct[i]] = weights[i] 70 | state[:,:,3] = 0 71 | state[:,:,4] = error 72 | return state.transpose(2, 0, 1) 73 | 74 | sac_agent = SAC.load("sac_mlp_large") 75 | sac_cnn_agent = SAC.load("sac_cnn_20_20_150") 76 | opter = tf.keras.optimizers.Adam(lr=0.01) 77 | 78 | y_test = np.expand_dims(y_test, axis=-1) 79 | y_train = np.expand_dims(y_train, axis=-1) 80 | 81 | mlp_mins_train = [] 82 | cnn_mins_train = [] 83 | grad_mins_train = [] 84 | mixed_mins_train = [] 85 | mlp_mins_val = [] 86 | cnn_mins_val = [] 87 | grad_mins_val = [] 88 | mixed_mins_val = [] 89 | 90 | rep = 3 91 | 92 | for it in range(rep): 93 | print(it) 94 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 95 | layer1 = tfq.layers.NoisyPQC(c, readout_operators, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 96 | vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 97 | vqc.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), optimizer=tf.keras.optimizers.Adam(lr=0.01), metrics=['acc']) 98 | 99 | loss_fn = tf.keras.losses.BinaryCrossentropy(from_logits=True) 100 | 101 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 102 | layer1 = tfq.layers.NoisyPQC(c, readout_operators, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 103 | sac_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 104 | 105 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 106 | layer1 = tfq.layers.NoisyPQC(c, readout_operators, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 107 | sac_cnn_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 108 | 109 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 110 | layer1 = tfq.layers.NoisyPQC(c, readout_operators, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 111 | mixed = tf.keras.models.Model(inputs=inputs, outputs=layer1) 112 | 113 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 114 | layer1 = tfq.layers.NoisyPQC(c, readout_operators, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 115 | mixed_test = tf.keras.models.Model(inputs=inputs, outputs=layer1) 116 | mixed_test.set_weights(vqc.get_weights()) 117 | 118 | sac_vqc.set_weights(vqc.get_weights()) 119 | sac_cnn_vqc.set_weights(vqc.get_weights()) 120 | mixed.set_weights(vqc.get_weights()) 121 | history = vqc.fit(X_train, y_train, epochs=es, batch_size=bs, validation_data=(X_test, y_test)) 122 | 123 | sac_loss = [] 124 | sac_val_loss = [] 125 | sac_cnn_loss = [] 126 | sac_cnn_val_loss = [] 127 | mixed_loss = [] 128 | mixed_val_loss = [] 129 | 130 | for i in range(es): 131 | print(i, es) 132 | indexes = np.random.choice(len(X_train), len(X_train)) 133 | 134 | # SAC 135 | sac_error = loss_fn(y_train[indexes], sac_vqc(tf.gather(X_train, indexes))).numpy() 136 | sac_enc = encoding(400, 2, d * 2, [1, 1, 2, 2] * d, sac_vqc.trainable_variables[0].numpy(), sac_error) 137 | action, _ = sac_agent.predict(sac_enc) 138 | sac_vqc.set_weights([action[:vqc.trainable_variables[0].shape[0]]]) 139 | 140 | # SAC_CNN 141 | sac_cnn_error = loss_fn(y_train[indexes], sac_cnn_vqc(tf.gather(X_train, indexes))).numpy() 142 | sac_cnn_enc = cnn_enc(20, 20, 2, [1, 1, 2, 2] * d, sac_cnn_vqc.trainable_variables[0].numpy(), sac_cnn_error) 143 | action, _ = sac_cnn_agent.predict(sac_cnn_enc) 144 | sac_cnn_vqc.set_weights([action[:vqc.trainable_variables[0].shape[0]]]) 145 | 146 | with tf.GradientTape() as tape: 147 | loss = loss_fn(y_train[indexes], mixed(tf.gather(X_train, indexes))) 148 | grads = tape.gradient(loss, mixed.trainable_variables) 149 | opter.apply_gradients(zip(grads, mixed.trainable_variables)) 150 | 151 | sac_loss.append(loss_fn(y_train, sac_vqc(X_train)).numpy()) 152 | sac_val_loss.append(loss_fn(y_test, sac_vqc(X_test)).numpy()) 153 | sac_cnn_loss.append(loss_fn(y_train, sac_cnn_vqc(X_train)).numpy()) 154 | sac_cnn_val_loss.append(loss_fn(y_test, sac_cnn_vqc(X_test)).numpy()) 155 | 156 | sac_enc = encoding(400, 2, d * 2, [1, 1, 2, 2] * d, sac_vqc.trainable_variables[0].numpy(), loss.numpy()) 157 | mlp_action, _ = sac_agent.predict(sac_enc) 158 | mixed_test.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 159 | mlp_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 160 | sac_cnn_enc = cnn_enc(20, 20, 2, [1, 1, 2, 2] * d, sac_cnn_vqc.trainable_variables[0].numpy(), loss.numpy()) 161 | cnn_action, _ = sac_cnn_agent.predict(sac_cnn_enc) 162 | mixed_test.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 163 | cnn_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 164 | 165 | losses = [mlp_loss, cnn_loss, loss_fn(y_train, mixed(X_train)).numpy()] 166 | best = losses.index(min(losses)) 167 | if best == 0: 168 | mixed.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 169 | elif best == 1: 170 | mixed.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 171 | 172 | mixed_loss.append(loss_fn(y_train, mixed(X_train)).numpy()) 173 | mixed_val_loss.append(loss_fn(y_test, mixed(X_test)).numpy()) 174 | 175 | cnn_mins_val.append(min(sac_cnn_val_loss)) 176 | cnn_mins_train.append(min(sac_cnn_loss)) 177 | mlp_mins_train.append(min(sac_loss)) 178 | mlp_mins_val.append(min(sac_val_loss)) 179 | mixed_mins_val.append(min(mixed_val_loss)) 180 | mixed_mins_train.append(min(mixed_loss)) 181 | grad_mins_train.append(min(history.history['loss'])) 182 | grad_mins_val.append(min(history.history['val_loss'])) 183 | 184 | print("Training") 185 | print("SAC", np.mean(mlp_mins_train), np.std(mlp_mins_train), "SAC CNN", np.mean(cnn_mins_train), np.std(cnn_mins_train), "Gradient",\ 186 | np.mean(grad_mins_train), np.std(grad_mins_train), "Mixed", np.mean(mixed_mins_train), np.std(mixed_mins_train)) 187 | print("Validation") 188 | print("SAC", np.mean(mlp_mins_val), np.std(mlp_mins_val), "SAC CNN", np.mean(cnn_mins_val), np.std(cnn_mins_val), "Gradient",\ 189 | np.mean(grad_mins_val), np.std(grad_mins_val), "Mixed", np.mean(mixed_mins_val), np.std(mixed_mins_val)) 190 | -------------------------------------------------------------------------------- /testing/moons_noisy.py: -------------------------------------------------------------------------------- 1 | import tensorflow_quantum as tfq 2 | import tensorflow as tf 3 | import cirq 4 | import sympy 5 | import numpy as np 6 | from sklearn import datasets as ds 7 | from sklearn.model_selection import train_test_split 8 | from sklearn.preprocessing import MinMaxScaler 9 | import matplotlib.pyplot as plt 10 | from stable_baselines3 import SAC 11 | 12 | moon_data, moon_labels = ds.make_moons(300, noise=0.15, shuffle=True) 13 | moon_data = MinMaxScaler().fit_transform(moon_data) 14 | 15 | #plt.scatter(moon_data[moon_labels == 0][:,0], moon_data[moon_labels == 0][:,1], label='0', color='blue') 16 | #plt.scatter(moon_data[moon_labels == 1][:,0], moon_data[moon_labels == 1][:,1], label='1', color='red') 17 | #plt.show() 18 | 19 | # Quantum NN 20 | def convert_data(data, qubits, test=False): 21 | cs = [] 22 | for i in data: 23 | cir = cirq.Circuit() 24 | cir += cirq.rx(i[0] * np.pi).on(qubits[0]) 25 | cir += cirq.rz(i[0] * np.pi).on(qubits[0]) 26 | cir += cirq.rx(i[1] * np.pi).on(qubits[1]) 27 | cir += cirq.rz(i[1] * np.pi).on(qubits[1]) 28 | cs.append(cir) 29 | if test: 30 | return tfq.convert_to_tensor([cs]) 31 | return tfq.convert_to_tensor(cs) 32 | 33 | def encode(data, labels, qubits): 34 | X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=.2, random_state=42) 35 | return convert_data(X_train, qubits), convert_data(X_test, qubits), y_train, y_test 36 | 37 | def layer(circuit, qubits, params): 38 | circuit += cirq.CNOT(qubits[0], qubits[1]) 39 | circuit += cirq.ry(params[0]).on(qubits[0]) 40 | circuit += cirq.ry(params[1]).on(qubits[1]) 41 | circuit += cirq.rz(params[2]).on(qubits[0]) 42 | circuit += cirq.rz(params[3]).on(qubits[1]) 43 | return circuit 44 | 45 | def model_circuit(qubits, depth): 46 | cir = cirq.Circuit() 47 | num_params = depth * 4 48 | params = sympy.symbols("q0:%d"%num_params) 49 | for i in range(depth): 50 | cir = layer(cir, qubits, params[i * 4:i * 4 + 4]) 51 | return cir 52 | 53 | qs = [cirq.GridQubit(0, i) for i in range(2)] 54 | d = 6 55 | X_train, X_test, y_train, y_test = encode(moon_data, moon_labels, qs) 56 | c = model_circuit(qs, d) 57 | c = c.with_noise(cirq.depolarize(p=0.075)) 58 | readout_operators = [cirq.Z(qs[0]), cirq.Z(qs[1])] 59 | 60 | def encoding(size, num_q, num_d, struct, weights, error): 61 | state = np.zeros(shape=(size, 8)) 62 | for i in range(len(weights)): 63 | q = i % num_q 64 | #print(weights[i], self.struct[i], q, i//self.max_qubits, self.max_qubits, self.max_depth, 0 if self.ins is None else 1) 65 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, 0] 66 | return state.flatten() 67 | 68 | def cnn_enc(max_q, max_d, num_q, struct, weights, error): 69 | state = np.zeros(shape=(max_q, max_d, 5)) 70 | for i in range(len(weights)): 71 | qubit_number = i % num_q 72 | depth_number = i // num_q 73 | state[qubit_number][depth_number][struct[i]] = weights[i] 74 | state[:,:,3] = 0 75 | state[:,:,4] = error 76 | return state.transpose(2, 0, 1) 77 | 78 | #y_test = np.expand_dims(y_test, axis=-1) 79 | #y_train = np.expand_dims(y_train, axis=-1) 80 | 81 | loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) 82 | 83 | sac_agent = SAC.load("sac_mlp_large") 84 | sac_cnn_agent = SAC.load("sac_cnn_20_20_150") 85 | opter = tf.keras.optimizers.Adam(lr=0.01) 86 | 87 | es = 150 88 | bs = len(X_train) 89 | 90 | mlp_mins_train = [] 91 | cnn_mins_train = [] 92 | grad_mins_train = [] 93 | mixed_mins_train = [] 94 | mlp_mins_val = [] 95 | cnn_mins_val = [] 96 | grad_mins_val = [] 97 | mixed_mins_val = [] 98 | 99 | rep = 3 100 | for it in range(rep): 101 | print(it) 102 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 103 | layer1 = tfq.layers.NoisyPQC(c, readout_operators, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 104 | vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 105 | vqc.compile(loss=loss_fn, optimizer=tf.keras.optimizers.Adam(lr=0.01), metrics=['acc']) 106 | 107 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 108 | layer1 = tfq.layers.NoisyPQC(c, readout_operators, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 109 | sac_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 110 | 111 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 112 | layer1 = tfq.layers.NoisyPQC(c, readout_operators, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 113 | sac_cnn_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 114 | 115 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 116 | layer1 = tfq.layers.NoisyPQC(c, readout_operators, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 117 | mixed = tf.keras.models.Model(inputs=inputs, outputs=layer1) 118 | 119 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 120 | layer1 = tfq.layers.NoisyPQC(c, readout_operators, repetitions=1000, sample_based=True, differentiator=tfq.differentiators.ParameterShift())(inputs) 121 | mixed_test = tf.keras.models.Model(inputs=inputs, outputs=layer1) 122 | 123 | sac_vqc.set_weights(vqc.get_weights()) 124 | sac_cnn_vqc.set_weights(vqc.get_weights()) 125 | mixed.set_weights(vqc.get_weights()) 126 | history = vqc.fit(X_train, y_train, epochs=es, batch_size=bs, validation_data=(X_test, y_test)) 127 | 128 | sac_loss = [] 129 | sac_val_loss = [] 130 | sac_cnn_loss = [] 131 | sac_cnn_val_loss = [] 132 | mixed_loss = [] 133 | mixed_val_loss = [] 134 | 135 | for i in range(es): 136 | print(i, es) 137 | indexes = np.random.choice(len(X_train), len(X_train)) 138 | 139 | # SAC 140 | sac_error = loss_fn(y_train[indexes], sac_vqc(tf.gather(X_train, indexes))).numpy() 141 | sac_enc = encoding(400, 2, d * 2, [1, 1, 2, 2] * d, sac_vqc.trainable_variables[0].numpy(), sac_error) 142 | action, _ = sac_agent.predict(sac_enc) 143 | sac_vqc.set_weights([action[:vqc.trainable_variables[0].shape[0]]]) 144 | 145 | # SAC_CNN 146 | sac_cnn_error = loss_fn(y_train[indexes], sac_cnn_vqc(tf.gather(X_train, indexes))).numpy() 147 | sac_cnn_enc = cnn_enc(20, 20, 2, [1, 1, 2, 2] * d, sac_cnn_vqc.trainable_variables[0].numpy(), sac_cnn_error) 148 | action, _ = sac_cnn_agent.predict(sac_cnn_enc) 149 | sac_cnn_vqc.set_weights([action[:vqc.trainable_variables[0].shape[0]]]) 150 | 151 | with tf.GradientTape() as tape: 152 | loss = loss_fn(y_train[indexes], mixed(tf.gather(X_train, indexes))) 153 | grads = tape.gradient(loss, mixed.trainable_variables) 154 | opter.apply_gradients(zip(grads, mixed.trainable_variables)) 155 | 156 | sac_loss.append(loss_fn(y_train, sac_vqc(X_train)).numpy()) 157 | sac_val_loss.append(loss_fn(y_test, sac_vqc(X_test)).numpy()) 158 | sac_cnn_loss.append(loss_fn(y_train, sac_cnn_vqc(X_train)).numpy()) 159 | sac_cnn_val_loss.append(loss_fn(y_test, sac_cnn_vqc(X_test)).numpy()) 160 | 161 | sac_enc = encoding(400, 2, d * 2, [1, 1, 2, 2] * d, sac_vqc.trainable_variables[0].numpy(), loss.numpy()) 162 | mlp_action, _ = sac_agent.predict(sac_enc) 163 | mixed_test.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 164 | mlp_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 165 | sac_cnn_enc = cnn_enc(20, 20, 2, [1, 1, 2, 2] * d, sac_cnn_vqc.trainable_variables[0].numpy(), loss.numpy()) 166 | cnn_action, _ = sac_cnn_agent.predict(sac_cnn_enc) 167 | mixed_test.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 168 | cnn_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 169 | 170 | losses = [mlp_loss, cnn_loss, loss_fn(y_train, mixed(X_train)).numpy()] 171 | best = losses.index(min(losses)) 172 | if best == 0: 173 | mixed.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 174 | elif best == 1: 175 | mixed.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 176 | 177 | mixed_loss.append(loss_fn(y_train, mixed(X_train)).numpy()) 178 | mixed_val_loss.append(loss_fn(y_test, mixed(X_test)).numpy()) 179 | 180 | cnn_mins_val.append(min(sac_cnn_val_loss)) 181 | cnn_mins_train.append(min(sac_cnn_loss)) 182 | mlp_mins_train.append(min(sac_loss)) 183 | mlp_mins_val.append(min(sac_val_loss)) 184 | mixed_mins_val.append(min(mixed_val_loss)) 185 | mixed_mins_train.append(min(mixed_loss)) 186 | grad_mins_train.append(min(history.history['loss'])) 187 | grad_mins_val.append(min(history.history['val_loss'])) 188 | 189 | print("Training") 190 | print("SAC", np.mean(mlp_mins_train), np.std(mlp_mins_train), "SAC CNN", np.mean(cnn_mins_train), np.std(cnn_mins_train), "Gradient",\ 191 | np.mean(grad_mins_train), np.std(grad_mins_train), "Mixed", np.mean(mixed_mins_train), np.std(mixed_mins_train)) 192 | print("Validation") 193 | print("SAC", np.mean(mlp_mins_val), np.std(mlp_mins_val), "SAC CNN", np.mean(cnn_mins_val), np.std(cnn_mins_val), "Gradient",\ 194 | np.mean(grad_mins_val), np.std(grad_mins_val), "Mixed", np.mean(mixed_mins_val), np.std(mixed_mins_val)) 195 | -------------------------------------------------------------------------------- /testing/regression.py: -------------------------------------------------------------------------------- 1 | import tensorflow_quantum as tfq 2 | import tensorflow as tf 3 | import cirq 4 | import sympy 5 | import numpy as np 6 | from sklearn import datasets as ds 7 | from sklearn.model_selection import train_test_split 8 | from sklearn.preprocessing import MinMaxScaler 9 | from stable_baselines3 import SAC 10 | 11 | x, y = ds.load_boston(return_X_y=True) 12 | x = MinMaxScaler().fit_transform(x) 13 | y = (y - np.min(y)) / (np.max(y) - np.min(y)) 14 | 15 | # Quantum NN 16 | def convert_data(data, qubits, test=False): 17 | cs = [] 18 | for i in data: 19 | cir = cirq.Circuit() 20 | for j in range(len(qubits)): 21 | cir += cirq.rx(i[j] * np.pi).on(qubits[j]) 22 | cir += cirq.ry(i[j] * np.pi).on(qubits[j]) 23 | cs.append(cir) 24 | if test: 25 | return tfq.convert_to_tensor([cs]) 26 | return tfq.convert_to_tensor(cs) 27 | 28 | def encode(data, labels, qubits): 29 | X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=.2, random_state=43) 30 | return convert_data(X_train, qubits), convert_data(X_test, qubits), y_train, y_test 31 | 32 | def layer(circuit, qubits, params): 33 | for i in range(len(qubits)): 34 | if i + 1 < len(qubits): 35 | circuit += cirq.CNOT(qubits[i], qubits[i + 1]) 36 | circuit += cirq.ry(params[i]).on(qubits[i]) 37 | for i in range(len(qubits)): 38 | circuit += cirq.rz(params[i + len(qubits)]).on(qubits[i]) 39 | circuit += cirq.CNOT(qubits[-1], qubits[0]) 40 | return circuit 41 | 42 | def model_circuit(qubits, depth): 43 | cir = cirq.Circuit() 44 | num_params = depth * 2 * len(qubits) 45 | params = sympy.symbols("q0:%d"%num_params) 46 | for i in range(depth): 47 | cir = layer(cir, qubits, params[i * 2 * len(qubits):i * 2 * len(qubits) + 2 * len(qubits)]) 48 | return cir 49 | 50 | qs = [cirq.GridQubit(0, i) for i in range(13)] 51 | d = 3 52 | num_params = d * 2 * len(qs) 53 | X_train, X_test, y_train, y_test = encode(x, y, qs) 54 | c = model_circuit(qs, d) 55 | 56 | readout_operators = [cirq.Z(qs[0])] 57 | 58 | loss_fn = tf.keras.losses.MeanSquaredError() 59 | 60 | es = 150 61 | bs = len(X_train) 62 | 63 | def encoding(size, num_q, num_d, struct, weights, error): 64 | state = np.zeros(shape=(size, 8)) 65 | for i in range(len(weights)): 66 | q = i % num_q 67 | #print(weights[i], self.struct[i], q, i//self.max_qubits, self.max_qubits, self.max_depth, 0 if self.ins is None else 1) 68 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, 0] 69 | return state.flatten() 70 | 71 | def cnn_enc(max_q, max_d, num_q, struct, weights, error): 72 | state = np.zeros(shape=(max_q, max_d, 5)) 73 | for i in range(len(weights)): 74 | qubit_number = i % num_q 75 | depth_number = i // num_q 76 | state[qubit_number][depth_number][struct[i]] = weights[i] 77 | state[:,:,3] = 0 78 | state[:,:,4] = error 79 | return state.transpose(2, 0, 1) 80 | 81 | sac_agent = SAC.load("sac_mlp_large") 82 | sac_cnn_agent = SAC.load("sac_cnn_20_20_150") 83 | opter = tf.keras.optimizers.Adam(lr=0.01) 84 | 85 | mlp_mins_train = [] 86 | cnn_mins_train = [] 87 | grad_mins_train = [] 88 | mixed_mins_train = [] 89 | mlp_mins_val = [] 90 | cnn_mins_val = [] 91 | grad_mins_val = [] 92 | mixed_mins_val = [] 93 | 94 | rep = 3 95 | for it in range(rep): 96 | print(it) 97 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 98 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 99 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 100 | vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 101 | vqc.compile(loss=tf.keras.losses.MeanSquaredError(), optimizer=tf.keras.optimizers.Adam(lr=0.01)) 102 | 103 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 104 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 105 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 106 | sac_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 107 | 108 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 109 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 110 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 111 | sac_cnn_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 112 | 113 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 114 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 115 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 116 | mixed = tf.keras.models.Model(inputs=inputs, outputs=layer1) 117 | 118 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 119 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 120 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 121 | mixed_test = tf.keras.models.Model(inputs=inputs, outputs=layer1) 122 | 123 | mixed_test.set_weights(vqc.get_weights()) 124 | sac_vqc.set_weights(vqc.get_weights()) 125 | sac_cnn_vqc.set_weights(vqc.get_weights()) 126 | mixed.set_weights(vqc.get_weights()) 127 | 128 | sac_loss = [] 129 | sac_val_loss = [] 130 | sac_cnn_loss = [] 131 | sac_cnn_val_loss = [] 132 | mixed_loss = [] 133 | mixed_val_loss = [] 134 | 135 | for i in range(es): 136 | print(i, es) 137 | indexes = np.random.choice(len(X_train), len(X_train)) 138 | 139 | # SAC 140 | sac_error = loss_fn(y_train[indexes], sac_vqc(tf.gather(X_train, indexes))).numpy() 141 | sac_enc = encoding(400, 13, d * 2, ([1 for _ in range(13)] + [2 for _ in range(13)]) * d, sac_vqc.trainable_variables[0].numpy(), sac_error) 142 | action, _ = sac_agent.predict(sac_enc) 143 | sac_vqc.set_weights([action[:num_params]]) 144 | 145 | # SAC_CNN 146 | sac_cnn_error = loss_fn(y_train[indexes], sac_cnn_vqc(tf.gather(X_train, indexes))).numpy() 147 | sac_cnn_enc = cnn_enc(20, 20, 13, ([1 for _ in range(13)] + [2 for _ in range(13)]) * d, sac_cnn_vqc.trainable_variables[0].numpy(), sac_cnn_error) 148 | action, _ = sac_cnn_agent.predict(sac_cnn_enc) 149 | sac_cnn_vqc.set_weights([action[:num_params]]) 150 | 151 | with tf.GradientTape() as tape: 152 | loss = loss_fn(y_train[indexes], mixed(tf.gather(X_train, indexes))) 153 | grads = tape.gradient(loss, mixed.trainable_variables) 154 | opter.apply_gradients(zip(grads, mixed.trainable_variables)) 155 | 156 | sac_loss.append(loss_fn(y_train, sac_vqc(X_train)).numpy()) 157 | sac_val_loss.append(loss_fn(y_test, sac_vqc(X_test)).numpy()) 158 | sac_cnn_loss.append(loss_fn(y_train, sac_cnn_vqc(X_train)).numpy()) 159 | sac_cnn_val_loss.append(loss_fn(y_test, sac_cnn_vqc(X_test)).numpy()) 160 | 161 | sac_enc = encoding(400, 13, d * 2, ([1 for _ in range(13)] + [2 for _ in range(13)]) * d, sac_vqc.trainable_variables[0].numpy(), loss.numpy()) 162 | mlp_action, _ = sac_agent.predict(sac_enc) 163 | mixed_test.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 164 | mlp_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 165 | sac_cnn_enc = cnn_enc(20, 20, 13, ([1 for _ in range(13)] + [2 for _ in range(13)]) * d, sac_cnn_vqc.trainable_variables[0].numpy(), loss.numpy()) 166 | cnn_action, _ = sac_cnn_agent.predict(sac_cnn_enc) 167 | mixed_test.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 168 | cnn_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 169 | 170 | losses = [mlp_loss, cnn_loss, loss_fn(y_train, mixed(X_train)).numpy()] 171 | best = losses.index(min(losses)) 172 | if best == 0: 173 | mixed.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 174 | elif best == 1: 175 | mixed.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 176 | 177 | mixed_loss.append(loss_fn(y_train, mixed(X_train)).numpy()) 178 | mixed_val_loss.append(loss_fn(y_test, mixed(X_test)).numpy()) 179 | 180 | history = vqc.fit(X_train, y_train, epochs=es, batch_size=bs, validation_data=(X_test, y_test)) 181 | cnn_mins_val.append(min(sac_cnn_val_loss)) 182 | cnn_mins_train.append(min(sac_cnn_loss)) 183 | mlp_mins_train.append(min(sac_loss)) 184 | mlp_mins_val.append(min(sac_val_loss)) 185 | mixed_mins_val.append(min(mixed_val_loss)) 186 | mixed_mins_train.append(min(mixed_loss)) 187 | grad_mins_train.append(min(history.history['loss'])) 188 | grad_mins_val.append(min(history.history['val_loss'])) 189 | 190 | print("Training") 191 | print("$", np.mean(mlp_mins_train), "\pm", np.std(mlp_mins_train), "$ & $", np.mean(cnn_mins_train), "\pm", np.std(cnn_mins_train), "$ & $",\ 192 | np.mean(grad_mins_train), "\pm", np.std(grad_mins_train), "$ & $", np.mean(mixed_mins_train), "\pm", np.std(mixed_mins_train), "$") 193 | print("Validation") 194 | print("$", np.mean(mlp_mins_val), "\pm", np.std(mlp_mins_val), "$ & $", np.mean(cnn_mins_val), "\pm", np.std(cnn_mins_val), "$ & $",\ 195 | np.mean(grad_mins_val), "\pm", np.std(grad_mins_val), "$ & $", np.mean(mixed_mins_val), "\pm", np.std(mixed_mins_val), "$") 196 | -------------------------------------------------------------------------------- /testing/moons.py: -------------------------------------------------------------------------------- 1 | import tensorflow_quantum as tfq 2 | import tensorflow as tf 3 | import cirq 4 | import sympy 5 | import numpy as np 6 | from sklearn import datasets as ds 7 | from sklearn.model_selection import train_test_split 8 | from sklearn.preprocessing import MinMaxScaler 9 | import matplotlib.pyplot as plt 10 | from stable_baselines3 import SAC 11 | 12 | moon_data, moon_labels = ds.make_moons(300, noise=0.15, shuffle=True) 13 | moon_data = MinMaxScaler().fit_transform(moon_data) 14 | 15 | #plt.scatter(moon_data[moon_labels == 0][:,0], moon_data[moon_labels == 0][:,1], label='0', color='blue') 16 | #plt.scatter(moon_data[moon_labels == 1][:,0], moon_data[moon_labels == 1][:,1], label='1', color='red') 17 | #plt.show() 18 | 19 | # Quantum NN 20 | def convert_data(data, qubits, test=False): 21 | cs = [] 22 | for i in data: 23 | cir = cirq.Circuit() 24 | cir += cirq.rx(i[0] * np.pi).on(qubits[0]) 25 | cir += cirq.rz(i[0] * np.pi).on(qubits[0]) 26 | cir += cirq.rx(i[1] * np.pi).on(qubits[1]) 27 | cir += cirq.rz(i[1] * np.pi).on(qubits[1]) 28 | cs.append(cir) 29 | if test: 30 | return tfq.convert_to_tensor([cs]) 31 | return tfq.convert_to_tensor(cs) 32 | 33 | def encode(data, labels, qubits): 34 | X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=.2, random_state=42) 35 | return convert_data(X_train, qubits), convert_data(X_test, qubits), y_train, y_test 36 | 37 | def layer(circuit, qubits, params): 38 | circuit += cirq.CNOT(qubits[0], qubits[1]) 39 | circuit += cirq.ry(params[0]).on(qubits[0]) 40 | circuit += cirq.ry(params[1]).on(qubits[1]) 41 | circuit += cirq.rz(params[2]).on(qubits[0]) 42 | circuit += cirq.rz(params[3]).on(qubits[1]) 43 | return circuit 44 | 45 | def model_circuit(qubits, depth): 46 | cir = cirq.Circuit() 47 | num_params = depth * 4 48 | params = sympy.symbols("q0:%d"%num_params) 49 | for i in range(depth): 50 | cir = layer(cir, qubits, params[i * 4:i * 4 + 4]) 51 | return cir 52 | 53 | qs = [cirq.GridQubit(0, i) for i in range(2)] 54 | d = 6 55 | X_train, X_test, y_train, y_test = encode(moon_data, moon_labels, qs) 56 | c = model_circuit(qs, d) 57 | 58 | readout_operators = [cirq.Z(qs[0]), cirq.Z(qs[1])] 59 | 60 | def encoding(size, num_q, num_d, struct, weights, error): 61 | state = np.zeros(shape=(size, 8)) 62 | for i in range(len(weights)): 63 | q = i % num_q 64 | #print(weights[i], self.struct[i], q, i//self.max_qubits, self.max_qubits, self.max_depth, 0 if self.ins is None else 1) 65 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, 0] 66 | return state.flatten() 67 | 68 | def cnn_enc(max_q, max_d, num_q, struct, weights, error): 69 | state = np.zeros(shape=(max_q, max_d, 5)) 70 | for i in range(len(weights)): 71 | qubit_number = i % num_q 72 | depth_number = i // num_q 73 | state[qubit_number][depth_number][struct[i]] = weights[i] 74 | state[:,:,3] = 0 75 | state[:,:,4] = error 76 | return state.transpose(2, 0, 1) 77 | 78 | #y_test = np.expand_dims(y_test, axis=-1) 79 | #y_train = np.expand_dims(y_train, axis=-1) 80 | 81 | loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) 82 | 83 | sac_agent = SAC.load("sac_mlp_large") 84 | sac_cnn_agent = SAC.load("sac_cnn_20_20_150") 85 | opter = tf.keras.optimizers.Adam(lr=0.01) 86 | 87 | es = 150 88 | bs = len(X_train) 89 | 90 | mlp_mins_train = [] 91 | cnn_mins_train = [] 92 | grad_mins_train = [] 93 | mixed_mins_train = [] 94 | mlp_mins_val = [] 95 | cnn_mins_val = [] 96 | grad_mins_val = [] 97 | mixed_mins_val = [] 98 | 99 | rep = 3 100 | for it in range(rep): 101 | print(it) 102 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 103 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 104 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 105 | vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 106 | vqc.compile(loss=loss_fn, optimizer=tf.keras.optimizers.Adam(lr=0.01), metrics=['acc']) 107 | 108 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 109 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 110 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 111 | sac_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 112 | 113 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 114 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 115 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 116 | sac_cnn_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 117 | 118 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 119 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 120 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 121 | mixed = tf.keras.models.Model(inputs=inputs, outputs=layer1) 122 | 123 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 124 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 125 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 126 | mixed_test = tf.keras.models.Model(inputs=inputs, outputs=layer1) 127 | mixed_test.set_weights(vqc.get_weights()) 128 | 129 | sac_vqc.set_weights(vqc.get_weights()) 130 | sac_cnn_vqc.set_weights(vqc.get_weights()) 131 | mixed.set_weights(vqc.get_weights()) 132 | history = vqc.fit(X_train, y_train, epochs=es, batch_size=bs, validation_data=(X_test, y_test)) 133 | 134 | sac_loss = [] 135 | sac_val_loss = [] 136 | sac_cnn_loss = [] 137 | sac_cnn_val_loss = [] 138 | mixed_loss = [] 139 | mixed_val_loss = [] 140 | 141 | for i in range(es): 142 | print(i, es) 143 | indexes = np.random.choice(len(X_train), len(X_train)) 144 | 145 | # SAC 146 | sac_error = loss_fn(y_train[indexes], sac_vqc(tf.gather(X_train, indexes))).numpy() 147 | sac_enc = encoding(400, 2, d * 2, [1, 1, 2, 2] * d, sac_vqc.trainable_variables[0].numpy(), sac_error) 148 | action, _ = sac_agent.predict(sac_enc) 149 | sac_vqc.set_weights([action[:vqc.trainable_variables[0].shape[0]]]) 150 | 151 | # SAC_CNN 152 | sac_cnn_error = loss_fn(y_train[indexes], sac_cnn_vqc(tf.gather(X_train, indexes))).numpy() 153 | sac_cnn_enc = cnn_enc(20, 20, 2, [1, 1, 2, 2] * d, sac_cnn_vqc.trainable_variables[0].numpy(), sac_cnn_error) 154 | action, _ = sac_cnn_agent.predict(sac_cnn_enc) 155 | sac_cnn_vqc.set_weights([action[:vqc.trainable_variables[0].shape[0]]]) 156 | 157 | with tf.GradientTape() as tape: 158 | loss = loss_fn(y_train[indexes], mixed(tf.gather(X_train, indexes))) 159 | grads = tape.gradient(loss, mixed.trainable_variables) 160 | opter.apply_gradients(zip(grads, mixed.trainable_variables)) 161 | 162 | sac_loss.append(loss_fn(y_train, sac_vqc(X_train)).numpy()) 163 | sac_val_loss.append(loss_fn(y_test, sac_vqc(X_test)).numpy()) 164 | sac_cnn_loss.append(loss_fn(y_train, sac_cnn_vqc(X_train)).numpy()) 165 | sac_cnn_val_loss.append(loss_fn(y_test, sac_cnn_vqc(X_test)).numpy()) 166 | 167 | sac_enc = encoding(400, 2, d * 2, [1, 1, 2, 2] * d, sac_vqc.trainable_variables[0].numpy(), loss.numpy()) 168 | mlp_action, _ = sac_agent.predict(sac_enc) 169 | mixed_test.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 170 | mlp_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 171 | sac_cnn_enc = cnn_enc(20, 20, 2, [1, 1, 2, 2] * d, sac_cnn_vqc.trainable_variables[0].numpy(), loss.numpy()) 172 | cnn_action, _ = sac_cnn_agent.predict(sac_cnn_enc) 173 | mixed_test.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 174 | cnn_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 175 | 176 | losses = [mlp_loss, cnn_loss, loss_fn(y_train, mixed(X_train)).numpy()] 177 | best = losses.index(min(losses)) 178 | if best == 0: 179 | mixed.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 180 | elif best == 1: 181 | mixed.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 182 | 183 | mixed_loss.append(loss_fn(y_train, mixed(X_train)).numpy()) 184 | mixed_val_loss.append(loss_fn(y_test, mixed(X_test)).numpy()) 185 | 186 | cnn_mins_val.append(min(sac_cnn_val_loss)) 187 | cnn_mins_train.append(min(sac_cnn_loss)) 188 | mlp_mins_train.append(min(sac_loss)) 189 | mlp_mins_val.append(min(sac_val_loss)) 190 | mixed_mins_val.append(min(mixed_val_loss)) 191 | mixed_mins_train.append(min(mixed_loss)) 192 | grad_mins_train.append(min(history.history['loss'])) 193 | grad_mins_val.append(min(history.history['val_loss'])) 194 | 195 | print("Training") 196 | print("$", np.mean(mlp_mins_train), "\pm", np.std(mlp_mins_train), "$ & $", np.mean(cnn_mins_train), "\pm", np.std(cnn_mins_train), "$ & $",\ 197 | np.mean(grad_mins_train), "\pm", np.std(grad_mins_train), "$ & $", np.mean(mixed_mins_train), "\pm", np.std(mixed_mins_train), "$") 198 | print("Validation") 199 | print("$", np.mean(mlp_mins_val), "\pm", np.std(mlp_mins_val), "$ & $", np.mean(cnn_mins_val), "\pm", np.std(cnn_mins_val), "$ & $",\ 200 | np.mean(grad_mins_val), "\pm", np.std(grad_mins_val), "$ & $", np.mean(mixed_mins_val), "\pm", np.std(mixed_mins_val), "$") 201 | -------------------------------------------------------------------------------- /testing/circles.py: -------------------------------------------------------------------------------- 1 | import tensorflow_quantum as tfq 2 | import tensorflow as tf 3 | import cirq 4 | import sympy 5 | import numpy as np 6 | from sklearn import datasets as ds 7 | from sklearn.model_selection import train_test_split 8 | from sklearn.preprocessing import MinMaxScaler 9 | from stable_baselines3 import SAC 10 | import matplotlib.pyplot as plt 11 | 12 | circle_data, circle_labels = ds.make_circles(300, noise=0.2, factor=0.3, shuffle=True) 13 | circle_data = MinMaxScaler().fit_transform(circle_data) 14 | 15 | #plt.scatter(circle_data[circle_labels == 0][:,0], circle_data[circle_labels == 0][:,1], label='0', color='blue') 16 | #plt.scatter(circle_data[circle_labels == 1][:,0], circle_data[circle_labels == 1][:,1], label='1', color='red') 17 | #plt.show() 18 | 19 | # Quantum NN 20 | def convert_data(data, qubits, test=False): 21 | cs = [] 22 | for i in data: 23 | cir = cirq.Circuit() 24 | cir += cirq.rx(i[0] * np.pi).on(qubits[0]) 25 | cir += cirq.rz(i[0] * np.pi).on(qubits[0]) 26 | cir += cirq.rx(i[1] * np.pi).on(qubits[1]) 27 | cir += cirq.rz(i[1] * np.pi).on(qubits[1]) 28 | cs.append(cir) 29 | if test: 30 | return tfq.convert_to_tensor([cs]) 31 | return tfq.convert_to_tensor(cs) 32 | 33 | def encode(data, labels, qubits): 34 | X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=.2, random_state=43) 35 | return convert_data(X_train, qubits), convert_data(X_test, qubits), y_train, y_test 36 | 37 | def layer(circuit, qubits, params): 38 | circuit += cirq.CNOT(qubits[0], qubits[1]) 39 | circuit += cirq.ry(params[0]).on(qubits[0]) 40 | circuit += cirq.ry(params[1]).on(qubits[1]) 41 | circuit += cirq.rz(params[2]).on(qubits[0]) 42 | circuit += cirq.rz(params[3]).on(qubits[1]) 43 | return circuit 44 | 45 | def model_circuit(qubits, depth): 46 | cir = cirq.Circuit() 47 | num_params = depth * 4 48 | params = sympy.symbols("q0:%d"%num_params) 49 | for i in range(depth): 50 | cir = layer(cir, qubits, params[i * 4:i * 4 + 4]) 51 | return cir 52 | 53 | qs = [cirq.GridQubit(0, i) for i in range(2)] 54 | d = 6 55 | X_train, X_test, y_train, y_test = encode(circle_data, circle_labels, qs) 56 | c = model_circuit(qs, d) 57 | readout_operators = [cirq.Z(qs[0])] 58 | 59 | es = 150 60 | bs = len(X_train) 61 | 62 | def encoding(size, num_q, num_d, struct, weights, error): 63 | state = np.zeros(shape=(size, 8)) 64 | for i in range(len(weights)): 65 | q = i % num_q 66 | #print(weights[i], self.struct[i], q, i//self.max_qubits, self.max_qubits, self.max_depth, 0 if self.ins is None else 1) 67 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, 0] 68 | return state.flatten() 69 | 70 | def cnn_enc(max_q, max_d, num_q, struct, weights, error): 71 | state = np.zeros(shape=(max_q, max_d, 5)) 72 | for i in range(len(weights)): 73 | qubit_number = i % num_q 74 | depth_number = i // num_q 75 | state[qubit_number][depth_number][struct[i]] = weights[i] 76 | state[:,:,3] = 0 77 | state[:,:,4] = error 78 | return state.transpose(2, 0, 1) 79 | 80 | sac_agent = SAC.load("sac_mlp_large") 81 | sac_cnn_agent = SAC.load("sac_cnn_20_20_150") 82 | opter = tf.keras.optimizers.Adam(lr=0.01) 83 | 84 | y_test = np.expand_dims(y_test, axis=-1) 85 | y_train = np.expand_dims(y_train, axis=-1) 86 | 87 | mlp_mins_train = [] 88 | cnn_mins_train = [] 89 | grad_mins_train = [] 90 | mixed_mins_train = [] 91 | mlp_mins_val = [] 92 | cnn_mins_val = [] 93 | grad_mins_val = [] 94 | mixed_mins_val = [] 95 | 96 | rep = 3 97 | 98 | for it in range(rep): 99 | print(it) 100 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 101 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 102 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 103 | vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 104 | vqc.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), optimizer=tf.keras.optimizers.Adam(lr=0.01), metrics=['acc']) 105 | 106 | loss_fn = tf.keras.losses.BinaryCrossentropy(from_logits=True) 107 | 108 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 109 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 110 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 111 | sac_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 112 | 113 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 114 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 115 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 116 | sac_cnn_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 117 | 118 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 119 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 120 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 121 | mixed = tf.keras.models.Model(inputs=inputs, outputs=layer1) 122 | 123 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 124 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 125 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 126 | mixed_test = tf.keras.models.Model(inputs=inputs, outputs=layer1) 127 | mixed_test.set_weights(vqc.get_weights()) 128 | 129 | sac_vqc.set_weights(vqc.get_weights()) 130 | sac_cnn_vqc.set_weights(vqc.get_weights()) 131 | mixed.set_weights(vqc.get_weights()) 132 | history = vqc.fit(X_train, y_train, epochs=es, batch_size=bs, validation_data=(X_test, y_test)) 133 | 134 | sac_loss = [] 135 | sac_val_loss = [] 136 | sac_cnn_loss = [] 137 | sac_cnn_val_loss = [] 138 | mixed_loss = [] 139 | mixed_val_loss = [] 140 | 141 | for i in range(es): 142 | print(i, es) 143 | indexes = np.random.choice(len(X_train), len(X_train)) 144 | 145 | # SAC 146 | sac_error = loss_fn(y_train[indexes], sac_vqc(tf.gather(X_train, indexes))).numpy() 147 | sac_enc = encoding(400, 2, d * 2, [1, 1, 2, 2] * d, sac_vqc.trainable_variables[0].numpy(), sac_error) 148 | action, _ = sac_agent.predict(sac_enc) 149 | sac_vqc.set_weights([action[:vqc.trainable_variables[0].shape[0]]]) 150 | 151 | # SAC_CNN 152 | sac_cnn_error = loss_fn(y_train[indexes], sac_cnn_vqc(tf.gather(X_train, indexes))).numpy() 153 | sac_cnn_enc = cnn_enc(20, 20, 2, [1, 1, 2, 2] * d, sac_cnn_vqc.trainable_variables[0].numpy(), sac_cnn_error) 154 | action, _ = sac_cnn_agent.predict(sac_cnn_enc) 155 | sac_cnn_vqc.set_weights([action[:vqc.trainable_variables[0].shape[0]]]) 156 | 157 | with tf.GradientTape() as tape: 158 | loss = loss_fn(y_train[indexes], mixed(tf.gather(X_train, indexes))) 159 | grads = tape.gradient(loss, mixed.trainable_variables) 160 | opter.apply_gradients(zip(grads, mixed.trainable_variables)) 161 | 162 | sac_loss.append(loss_fn(y_train, sac_vqc(X_train)).numpy()) 163 | sac_val_loss.append(loss_fn(y_test, sac_vqc(X_test)).numpy()) 164 | sac_cnn_loss.append(loss_fn(y_train, sac_cnn_vqc(X_train)).numpy()) 165 | sac_cnn_val_loss.append(loss_fn(y_test, sac_cnn_vqc(X_test)).numpy()) 166 | 167 | sac_enc = encoding(400, 2, d * 2, [1, 1, 2, 2] * d, sac_vqc.trainable_variables[0].numpy(), loss.numpy()) 168 | mlp_action, _ = sac_agent.predict(sac_enc) 169 | mixed_test.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 170 | mlp_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 171 | sac_cnn_enc = cnn_enc(20, 20, 2, [1, 1, 2, 2] * d, sac_cnn_vqc.trainable_variables[0].numpy(), loss.numpy()) 172 | cnn_action, _ = sac_cnn_agent.predict(sac_cnn_enc) 173 | mixed_test.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 174 | cnn_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 175 | 176 | losses = [mlp_loss, cnn_loss, loss_fn(y_train, mixed(X_train)).numpy()] 177 | best = losses.index(min(losses)) 178 | if best == 0: 179 | mixed.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 180 | elif best == 1: 181 | mixed.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 182 | 183 | mixed_loss.append(loss_fn(y_train, mixed(X_train)).numpy()) 184 | mixed_val_loss.append(loss_fn(y_test, mixed(X_test)).numpy()) 185 | 186 | cnn_mins_val.append(min(sac_cnn_val_loss)) 187 | cnn_mins_train.append(min(sac_cnn_loss)) 188 | mlp_mins_train.append(min(sac_loss)) 189 | mlp_mins_val.append(min(sac_val_loss)) 190 | mixed_mins_val.append(min(mixed_val_loss)) 191 | mixed_mins_train.append(min(mixed_loss)) 192 | grad_mins_train.append(min(history.history['loss'])) 193 | grad_mins_val.append(min(history.history['val_loss'])) 194 | 195 | print("Training") 196 | print("$", np.mean(mlp_mins_train), "\pm", np.std(mlp_mins_train), "$ & $", np.mean(cnn_mins_train), "\pm", np.std(cnn_mins_train), "$ & $",\ 197 | np.mean(grad_mins_train), "\pm", np.std(grad_mins_train), "$ & $", np.mean(mixed_mins_train), "\pm", np.std(mixed_mins_train), "$") 198 | print("Validation") 199 | print("$", np.mean(mlp_mins_val), "\pm", np.std(mlp_mins_val), "$ & $", np.mean(cnn_mins_val), "\pm", np.std(cnn_mins_val), "$ & $",\ 200 | np.mean(grad_mins_val), "\pm", np.std(grad_mins_val), "$ & $", np.mean(mixed_mins_val), "\pm", np.std(mixed_mins_val), "$") 201 | -------------------------------------------------------------------------------- /testing/blobs.py: -------------------------------------------------------------------------------- 1 | import tensorflow_quantum as tfq 2 | import tensorflow as tf 3 | import cirq 4 | import sympy 5 | import numpy as np 6 | from sklearn import datasets as ds 7 | from sklearn.model_selection import train_test_split 8 | from sklearn.preprocessing import MinMaxScaler 9 | import matplotlib.pyplot as plt 10 | from stable_baselines3 import SAC 11 | 12 | 13 | blob_data, blob_labels = ds.make_blobs(1000, centers=7, shuffle=True) 14 | blob_data = MinMaxScaler().fit_transform(blob_data) 15 | 16 | #plt.scatter(blob_data[blob_labels == 0][:,0], blob_data[blob_labels == 0][:,1], label='0', color='blue') 17 | #plt.scatter(blob_data[blob_labels == 1][:,0], blob_data[blob_labels == 1][:,1], label='1', color='red') 18 | #plt.scatter(blob_data[blob_labels == 2][:,0], blob_data[blob_labels == 2][:,1], label='2', color='green') 19 | #plt.scatter(blob_data[blob_labels == 3][:,0], blob_data[blob_labels == 3][:,1], label='3', color='black') 20 | #plt.scatter(blob_data[blob_labels == 4][:,0], blob_data[blob_labels == 4][:,1], label='4', color='yellow') 21 | #plt.scatter(blob_data[blob_labels == 5][:,0], blob_data[blob_labels == 5][:,1], label='5', color='m') 22 | #plt.scatter(blob_data[blob_labels == 6][:,0], blob_data[blob_labels == 6][:,1], label='6', color='c') 23 | #plt.show() 24 | 25 | # Quantum NN 26 | def convert_data(data, qubits, test=False): 27 | cs = [] 28 | for i in data: 29 | cir = cirq.Circuit() 30 | for j in qubits: 31 | cir += cirq.rx(i[0] * np.pi).on(j) 32 | cir += cirq.ry(i[1] * np.pi).on(j) 33 | cs.append(cir) 34 | if test: 35 | return tfq.convert_to_tensor([cs]) 36 | return tfq.convert_to_tensor(cs) 37 | 38 | def encode(data, labels, qubits): 39 | X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=.2, random_state=43) 40 | return convert_data(X_train, qubits), convert_data(X_test, qubits), y_train, y_test 41 | 42 | def layer(circuit, qubits, params): 43 | for i in range(len(qubits)): 44 | if i + 1 < len(qubits): 45 | circuit += cirq.CNOT(qubits[i], qubits[i + 1]) 46 | circuit += cirq.rz(params[i * 2]).on(qubits[i]) 47 | for i in range(len(qubits)): 48 | circuit += cirq.rx(params[i * 2 + 1]).on(qubits[i]) 49 | return circuit 50 | 51 | def model_circuit(qubits, depth): 52 | cir = cirq.Circuit() 53 | num_params = depth * 2 * len(qubits) 54 | params = sympy.symbols("q0:%d"%num_params) 55 | for i in range(depth): 56 | cir = layer(cir, qubits, params[i * 2 * len(qubits):i * 2 * len(qubits) + 2 * len(qubits)]) 57 | return cir 58 | 59 | q = 7 60 | qs = [cirq.GridQubit(0, i) for i in range(q)] 61 | d = 5 62 | X_train, X_test, y_train, y_test = encode(blob_data, blob_labels, qs) 63 | c = model_circuit(qs, d) 64 | 65 | readout_operators = [cirq.Z(i) for i in qs] 66 | 67 | loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) 68 | 69 | es = 150 70 | bs = len(X_train) 71 | 72 | def encoding(size, num_q, num_d, struct, weights, error): 73 | state = np.zeros(shape=(size, 8)) 74 | for i in range(len(weights)): 75 | q = i % num_q 76 | #print(weights[i], self.struct[i], q, i//self.max_qubits, self.max_qubits, self.max_depth, 0 if self.ins is None else 1) 77 | state[i] = [-error, weights[i], struct[i], q, i//num_q, num_q, num_d, 0] 78 | return state.flatten() 79 | 80 | def cnn_enc(max_q, max_d, num_q, struct, weights, error): 81 | state = np.zeros(shape=(max_q, max_d, 5)) 82 | for i in range(len(weights)): 83 | qubit_number = i % num_q 84 | depth_number = i // num_q 85 | state[qubit_number][depth_number][struct[i]] = weights[i] 86 | state[:,:,3] = 0 87 | state[:,:,4] = error 88 | return state.transpose(2, 0, 1) 89 | 90 | sac_agent = SAC.load("sac_mlp_large") 91 | sac_cnn_agent = SAC.load("sac_cnn_20_20_150") 92 | opter = tf.keras.optimizers.Adam(lr=0.01) 93 | 94 | mlp_mins_train = [] 95 | cnn_mins_train = [] 96 | grad_mins_train = [] 97 | mixed_mins_train = [] 98 | mlp_mins_val = [] 99 | cnn_mins_val = [] 100 | grad_mins_val = [] 101 | mixed_mins_val = [] 102 | 103 | rep = 3 104 | for it in range(rep): 105 | print(it) 106 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 107 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 108 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 109 | vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 110 | vqc.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), optimizer=tf.keras.optimizers.Adam(lr=0.01), metrics=['acc']) 111 | 112 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 113 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 114 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 115 | sac_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 116 | 117 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 118 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 119 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 120 | sac_cnn_vqc = tf.keras.models.Model(inputs=inputs, outputs=layer1) 121 | 122 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 123 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 124 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 125 | mixed = tf.keras.models.Model(inputs=inputs, outputs=layer1) 126 | 127 | inputs = tf.keras.Input(shape=(), dtype=tf.dtypes.string) 128 | #layer1 = tfq.layers.PQC(c, readout_operators, repetitions=1000, differentiator=tfq.differentiators.ParameterShift())(inputs) 129 | layer1 = tfq.layers.PQC(c, readout_operators, differentiator=tfq.differentiators.Adjoint())(inputs) 130 | mixed_test = tf.keras.models.Model(inputs=inputs, outputs=layer1) 131 | mixed_test.set_weights(vqc.get_weights()) 132 | 133 | sac_vqc.set_weights(vqc.get_weights()) 134 | sac_cnn_vqc.set_weights(vqc.get_weights()) 135 | mixed.set_weights(vqc.get_weights()) 136 | 137 | sac_loss = [] 138 | sac_val_loss = [] 139 | sac_cnn_loss = [] 140 | sac_cnn_val_loss = [] 141 | mixed_loss = [] 142 | mixed_val_loss = [] 143 | 144 | for i in range(es): 145 | print(i, es) 146 | indexes = np.random.choice(len(X_train), len(X_train)) 147 | 148 | # SAC 149 | sac_error = loss_fn(y_train[indexes], sac_vqc(tf.gather(X_train, indexes))).numpy() 150 | sac_enc = encoding(400, 7, d * 2, ([0 for _ in range(q)] + [2 for _ in range(q)]) * d, sac_vqc.trainable_variables[0].numpy(), sac_error) 151 | action, _ = sac_agent.predict(sac_enc) 152 | sac_vqc.set_weights([action[:vqc.trainable_variables[0].shape[0]]]) 153 | 154 | # SAC_CNN 155 | sac_cnn_error = loss_fn(y_train[indexes], sac_cnn_vqc(tf.gather(X_train, indexes))).numpy() 156 | sac_cnn_enc = cnn_enc(20, 20, 5, ([0 for _ in range(q)] + [2 for _ in range(q)]) * d, sac_cnn_vqc.trainable_variables[0].numpy(), sac_cnn_error) 157 | action, _ = sac_cnn_agent.predict(sac_cnn_enc) 158 | sac_cnn_vqc.set_weights([action[:vqc.trainable_variables[0].shape[0]]]) 159 | 160 | with tf.GradientTape() as tape: 161 | loss = loss_fn(y_train[indexes], mixed(tf.gather(X_train, indexes))) 162 | grads = tape.gradient(loss, mixed.trainable_variables) 163 | opter.apply_gradients(zip(grads, mixed.trainable_variables)) 164 | 165 | sac_loss.append(loss_fn(y_train, sac_vqc(X_train)).numpy()) 166 | sac_val_loss.append(loss_fn(y_test, sac_vqc(X_test)).numpy()) 167 | sac_cnn_loss.append(loss_fn(y_train, sac_cnn_vqc(X_train)).numpy()) 168 | sac_cnn_val_loss.append(loss_fn(y_test, sac_cnn_vqc(X_test)).numpy()) 169 | 170 | sac_enc = encoding(400, 5, d * 2, ([0 for _ in range(q)] + [2 for _ in range(q)]) * d, sac_vqc.trainable_variables[0].numpy(), loss.numpy()) 171 | mlp_action, _ = sac_agent.predict(sac_enc) 172 | mixed_test.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 173 | mlp_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 174 | sac_cnn_enc = cnn_enc(20, 20, 5, ([0 for _ in range(q)] + [2 for _ in range(q)]) * d, sac_cnn_vqc.trainable_variables[0].numpy(), loss.numpy()) 175 | cnn_action, _ = sac_cnn_agent.predict(sac_cnn_enc) 176 | mixed_test.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 177 | cnn_loss = loss_fn(y_train, mixed_test(X_train)).numpy() 178 | 179 | losses = [mlp_loss, cnn_loss, loss_fn(y_train, mixed(X_train)).numpy()] 180 | best = losses.index(min(losses)) 181 | if best == 0: 182 | mixed.set_weights([mlp_action[:vqc.trainable_variables[0].shape[0]]]) 183 | elif best == 1: 184 | mixed.set_weights([cnn_action[:vqc.trainable_variables[0].shape[0]]]) 185 | 186 | mixed_loss.append(loss_fn(y_train, mixed(X_train)).numpy()) 187 | mixed_val_loss.append(loss_fn(y_test, mixed(X_test)).numpy()) 188 | 189 | history = vqc.fit(X_train, y_train, epochs=es, batch_size=bs, validation_data=(X_test, y_test)) 190 | cnn_mins_val.append(min(sac_cnn_val_loss)) 191 | cnn_mins_train.append(min(sac_cnn_loss)) 192 | mlp_mins_train.append(min(sac_loss)) 193 | mlp_mins_val.append(min(sac_val_loss)) 194 | mixed_mins_val.append(min(mixed_val_loss)) 195 | mixed_mins_train.append(min(mixed_loss)) 196 | grad_mins_train.append(min(history.history['loss'])) 197 | grad_mins_val.append(min(history.history['val_loss'])) 198 | 199 | print("Training") 200 | print("$", np.mean(mlp_mins_train), "\pm", np.std(mlp_mins_train), "$ & $", np.mean(cnn_mins_train), "\pm", np.std(cnn_mins_train), "$ & $",\ 201 | np.mean(grad_mins_train), "\pm", np.std(grad_mins_train), "$ & $", np.mean(mixed_mins_train), "\pm", np.std(mixed_mins_train), "$") 202 | print("Validation") 203 | print("$", np.mean(mlp_mins_val), "\pm", np.std(mlp_mins_val), "$ & $", np.mean(cnn_mins_val), "\pm", np.std(cnn_mins_val), "$ & $",\ 204 | np.mean(grad_mins_val), "\pm", np.std(grad_mins_val), "$ & $", np.mean(mixed_mins_val), "\pm", np.std(mixed_mins_val), "$") 205 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------