def test_tf(self, tol): """Test gradient of multiple trainable Hamiltonian coefficients using tf""" tf = pytest.importorskip("tensorflow") coeffs1 = tf.Variable([0.1, 0.2, 0.3], dtype=tf.float64) coeffs2 = tf.Variable([0.7], dtype=tf.float64) weights = tf.Variable([0.4, 0.5], dtype=tf.float64) dev = qml.device("default.qubit.tf", wires=2) with tf.GradientTape() as t: jac = self.cost_fn(weights, coeffs1, coeffs2, dev=dev) expected = self.cost_fn_expected(weights.numpy(), coeffs1.numpy(), coeffs2.numpy()) assert np.allclose(jac, expected, atol=tol, rtol=0) # second derivative wrt to Hamiltonian coefficients should be zero hess = t.jacobian(jac, [coeffs1, coeffs2]) assert np.allclose(hess[0][:, 2:5], np.zeros([2, 3, 3]), atol=tol, rtol=0) assert np.allclose(hess[1][:, -1], np.zeros([2, 1, 1]), atol=tol, rtol=0)
def test_torch(self, tol): """Test gradient of multiple trainable Hamiltonian coefficients using torch""" torch = pytest.importorskip("torch") coeffs1 = torch.tensor([0.1, 0.2, 0.3], dtype=torch.float64, requires_grad=True) coeffs2 = torch.tensor([0.7], dtype=torch.float64, requires_grad=True) weights = torch.tensor([0.4, 0.5], dtype=torch.float64, requires_grad=True) dev = qml.device("default.qubit.torch", wires=2) res = self.cost_fn(weights, coeffs1, coeffs2, dev=dev) expected = self.cost_fn_expected(weights.detach().numpy(), coeffs1.detach().numpy(), coeffs2.detach().numpy()) assert np.allclose(res.detach(), expected, atol=tol, rtol=0) # second derivative wrt to Hamiltonian coefficients should be zero hess = torch.autograd.functional.jacobian( lambda *args: self.cost_fn(*args, dev=dev), (weights, coeffs1, coeffs2)) assert np.allclose(hess[1][:, 2:5], np.zeros([2, 3, 3]), atol=tol, rtol=0) assert np.allclose(hess[2][:, -1], np.zeros([2, 1, 1]), atol=tol, rtol=0)
def test_zero_dy(self): """A zero dy vector will return a zero matrix""" dy = np.zeros([2, 2]) jac = np.array([[[1.0, 0.1, 0.2], [0.2, 0.6, 0.1]], [[0.4, -0.7, 1.2], [-0.5, -0.6, 0.7]]]) vjp = qml.gradients.compute_vjp(dy, jac) assert np.all(vjp == np.zeros([3]))
def obtain_best_pipelines(dataframe): all_pipelines = sorted(dataframe['pipeline'].unique(), key=pipeline_sorting_key) num_pipelines = len(all_pipelines) best_pipeline = onp.zeros((num_shots, num_noise_rates), dtype=object) best_pipeline_id = onp.zeros((num_shots, num_noise_rates), dtype=int) vert = np.zeros((num_shots, num_noise_rates - 1), dtype=bool) horz = np.zeros((num_shots - 1, num_noise_rates), dtype=bool) best_df = pd.DataFrame() cumulative_q = {pipe: 0. for pipe in all_pipelines} for i, shots in tqdm(enumerate(all_shots), total=num_shots): for j, bnr in enumerate(all_noise_rates): this_noise_df = same_noise(dataframe, bnr, shots) # This loop only is relevant to the cumulative_q score, which currently is not used anymore. for pipeline in all_pipelines: # If the pipeline was deleted from the cumulative score because it failed # for a different noise setting, ignore it here. if pipeline not in cumulative_q: continue q = same_pipeline(this_noise_df, pipeline).q.item() if not np.isnan(q): cumulative_q[pipeline] += q else: # Remove the pipeline from the cumulative score del cumulative_q[pipeline] this_noise_best_df = best_by_rounded_q(this_noise_df, num_decimals) # Store the Series belonging to the best pipeline to the global best_df best_df = best_df.append(this_noise_best_df, ignore_index=True) # Store the best pipeline name and whether to draw boundaries in the cell heatmap best_pipeline[i, j] = this_noise_best_df.pipeline if j > 0 and best_pipeline[i, j - 1] != best_pipeline[i, j]: vert[i, j - 1] = True if i > 0 and best_pipeline[i - 1, j] != best_pipeline[i, j]: horz[i - 1, j] = True # Create a map of ids, mapping a pipeline name to an integer id, for the pipelines that occur in best_pipeline. pipeline_ids = { pipe: i for i, pipe in enumerate( [pipe for pipe in all_pipelines if pipe in best_pipeline]) } # Apply this map to the best_pipeline array for i, shots in enumerate(all_shots): for j, bnr in enumerate(all_noise_rates): best_pipeline_id[i, j] = pipeline_ids[best_pipeline[i, j]] return pipeline_ids, best_pipeline, best_pipeline_id, vert, horz, best_df
def test_vacuum_state(self): """Test the vacuum state is correct.""" self.logTestName() wires = 3 means, cov = vacuum_state(wires, hbar=hbar) self.assertAllAlmostEqual(means, np.zeros([2*wires]), delta=self.tol) self.assertAllAlmostEqual(cov, np.identity(2*wires)*hbar/2, delta=self.tol)
def _fast(x_new, A_samples, B_samples, featmap, pars, n_inp): """ Implements the fidelity measurement circuit using the "overlap with 0" trick. """ # Allocate registers dev = qml.device('default.qubit', wires=n_inp) # Identify input register wires wires = list(range(n_inp)) Proj0 = np.zeros((2**n_inp, 2**n_inp)) Proj0[0, 0] = 1 @qml.qnode(dev) def circuit(weights, x1=None, x2=None): # Apply embedding featmap(weights, x1, wires) # Apply inverse embedding featmap(negate(weights), negate(x2), wires) # Measure overlap with |0..0> return qml.expval(qml.Hermitian(Proj0, wires=wires)) # Compute mean overlap with A overlap_A = 0 for a in A_samples: overlap_A += circuit(pars, x1=a, x2=x_new) overlap_A = overlap_A / len(A_samples) # Compute mean overlap with B overlap_B = 0 for b in B_samples: overlap_B += circuit(pars, x1=b, x2=x_new) overlap_B = overlap_B / len(B_samples) return overlap_A, overlap_B
def calculate_classical_shadow(circuit_template, params, shadow_size, num_qubits): """ Given a circuit, creates a collection of snapshots consisting of a bit string and the index of a unitary operation. Args: circuit_template (function): A Pennylane QNode. params (array): Circuit parameters. shadow_size (int): The number of snapshots in the shadow. num_qubits (int): The number of qubits in the circuit. Returns: Tuple of two numpy arrays. The first array contains measurement outcomes (-1, 1) while the second array contains the index for the sampled Pauli's (0,1,2=X,Y,Z). Each row of the arrays corresponds to a distinct snapshot or sample while each column corresponds to a different qubit. """ # applying the single-qubit Clifford circuit is equivalent to measuring a Pauli unitary_ensemble = [qml.PauliX, qml.PauliY, qml.PauliZ] # sample random Pauli measurements uniformly, where 0,1,2 = X,Y,Z unitary_ids = np.random.randint(0, 3, size=(shadow_size, num_qubits)) outcomes = np.zeros((shadow_size, num_qubits)) for ns in range(shadow_size): # for each snapshot, add a random Pauli observable at each location obs = [unitary_ensemble[int(unitary_ids[ns, i])](i) for i in range(num_qubits)] outcomes[ns, :] = circuit_template(params, observable=obs) # combine the computational basis outcomes and the sampled unitaries return (outcomes, unitary_ids)
def test_kernel(binary_matrix, result): r"""Test that _kernel returns the correct result.""" # get the kernel from the gaussian elimination. pivots = (binary_matrix.T != 0).argmax(axis=0) nonpivots = np.setdiff1d(range(len(binary_matrix[0])), pivots) kernel = [] for col in nonpivots: col_vector = binary_matrix[:, col] null_vector = np.zeros((binary_matrix.shape[1]), dtype=int) null_vector[col] = 1 for i in pivots: first_entry = np.where(binary_matrix[:, i] == 1)[0][0] if col_vector[first_entry] == 1: null_vector[i] = 1 kernel.append(null_vector.tolist()) # get the nullspace from the _kernel function. nullspace = _kernel(binary_matrix) for nullvec in kernel: assert nullvec in nullspace.tolist() assert (nullspace == result).all()
def natural_gradient(params): """Calculate the natural gradient of the qnode() cost function. The code you write for this challenge should be completely contained within this function between the # QHACK # comment markers. You should evaluate the metric tensor and the gradient of the QNode, and then combine these together using the natural gradient definition. The natural gradient should be returned as a NumPy array. The metric tensor should be evaluated using the equation provided in the problem text. Hint: you will need to define a new QNode that returns the quantum state before measurement. Args: params (np.ndarray): Input parameters, of dimension 6 Returns: np.ndarray: The natural gradient evaluated at the input parameters, of dimension 6 """ natural_grad = np.zeros(6) # QHACK # # QHACK # return natural_grad
def test_no_readout_correction(self): """Test the QPU plugin with no readout correction""" device = np.random.choice(VALID_QPU_LATTICES) dev_qpu = qml.device( "forest.qpu", device=device, load_qc=False, readout_error=[0.9, 0.75], symmetrize_readout=None, calibrate_readout=None, ) qubit = 0 # just run program on the first qubit @qml.qnode(dev_qpu) def circuit_Xpl(): qml.RY(np.pi / 2, wires=qubit) return qml.expval(qml.PauliX(qubit)) @qml.qnode(dev_qpu) def circuit_Xmi(): qml.RY(-np.pi / 2, wires=qubit) return qml.expval(qml.PauliX(qubit)) @qml.qnode(dev_qpu) def circuit_Ypl(): qml.RX(-np.pi / 2, wires=qubit) return qml.expval(qml.PauliY(qubit)) @qml.qnode(dev_qpu) def circuit_Ymi(): qml.RX(np.pi / 2, wires=qubit) return qml.expval(qml.PauliY(qubit)) @qml.qnode(dev_qpu) def circuit_Zpl(): qml.RX(0.0, wires=qubit) return qml.expval(qml.PauliZ(qubit)) @qml.qnode(dev_qpu) def circuit_Zmi(): qml.RX(np.pi, wires=qubit) return qml.expval(qml.PauliZ(qubit)) num_expts = 10 results_unavged = np.zeros((num_expts, 6)) for i in range(num_expts): results_unavged[i, :] = [ circuit_Xpl(), circuit_Ypl(), circuit_Zpl(), circuit_Xmi(), circuit_Ymi(), circuit_Zmi(), ] results = np.mean(results_unavged, axis=0) assert np.allclose(results[:3], 0.8, atol=2e-2) assert np.allclose(results[3:], -0.5, atol=2e-2)
def _binary_matrix(terms, num_qubits): r"""Get a binary matrix representation of the Hamiltonian where each row corresponds to a Pauli term, which is represented by a concatenation of Z and X vectors. Args: terms (Iterable[Observable]): operators defining the Hamiltonian num_qubits (int): number of wires required to define the Hamiltonian Returns: array[int]: binary matrix representation of the Hamiltonian of shape :math:`len(terms) \times 2*num_qubits` **Example** >>> terms = [qml.PauliZ(wires=[0]) @ qml.PauliX(wires=[1]), ... qml.PauliZ(wires=[0]) @ qml.PauliY(wires=[2]), ... qml.PauliX(wires=[0]) @ qml.PauliY(wires=[3])] >>> _binary_matrix(terms, 4) array([[1, 0, 0, 0, 0, 1, 0, 0], [1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 0, 1, 1, 0, 0, 1]]) """ binary_matrix = np.zeros((len(terms), 2 * num_qubits), dtype=int) for idx, term in enumerate(terms): ops, wires = term.name, term.wires if len(wires) == 1: ops = [ops] for op, wire in zip(ops, wires): if op in ["PauliX", "PauliY"]: binary_matrix[idx][wire + num_qubits] = 1 if op in ["PauliZ", "PauliY"]: binary_matrix[idx][wire] = 1 return binary_matrix
def layer_starting_weights(self): random_weights = np.zeros(self.n_weights_per_layer) for i in range(self.n_qubits): random_weights[i * 6 + 3:(i + 1) * 6] = np.random.normal(loc=0, scale=1, size=3) return random_weights
def parameter_shift(qnode, params): gradients = np.zeros([len(params)]) for i in range(len(params)): gradients[i] = parameter_shift_term(qnode, params, i) return gradients
def error_wire(circuit_output): """Function that returns an error readout. Args: - circuit_output (np.ndarray): the density matrix of the `circuit` function. Returns: - (np.ndarray): a length-4 array that reveals the statistics of the error channel. It should display your algorithm's statistical prediction for whether an error occurred on wire `k` (k in {1,2,3}). The zeroth element represents the probability that a bitflip error does not occur. e.g., [0.28, 0.0, 0.72, 0.0] means a 28% chance no bitflip error occurs, but if one does occur it occurs on qubit #2 with a 72% chance. """ # QHACK # # process the circuit output here and return which qubit was the victim of a bitflip error! # extract probabilities from density matrix error_readout = np.zeros(4) for i in range(len(error_readout)): error_readout[i] = np.abs(circuit_output[i,i]) + np.abs(circuit_output[-i-1,-i-1]) # swap to follow pennylane's ordering tmp = error_readout[3] error_readout[3] = error_readout[1] error_readout[1] = tmp return error_readout
def thermal_state(self): """Test the thermal state is correct.""" self.logTestName() nbar = 0.5342 means, cov = thermal_state(nbar, hbar=hbar) self.assertAllAlmostEqual(means, np.zeros([2]), delta=self.tol) self.assertTrue(np.all((cov.diag * 2 / hbar - 1) / 2 == nbar))
def test_adjoint_hessian(self, tol): """Since the adjoint hessian is not a differentiable transform, higher-order derivatives are not supported.""" dev = qml.device("default.qubit.autograd", wires=2) params = np.array([0.543, -0.654], requires_grad=True) def cost_fn(x): with qml.tape.JacobianTape() as tape: qml.RX(x[0], wires=[0]) qml.RY(x[1], wires=[1]) qml.CNOT(wires=[0, 1]) qml.expval(qml.PauliZ(0)) return execute( [tape], dev, gradient_fn="device", gradient_kwargs={ "method": "adjoint_jacobian", "use_device_state": True }, )[0] with pytest.warns(UserWarning, match="Output seems independent"): res = qml.jacobian(qml.grad(cost_fn))(params) assert np.allclose(res, np.zeros([2, 2]), atol=tol, rtol=0)
def test_multiple_squeezing_gradient(self, mocker, tol): """Test that the gradient of a circuit with two squeeze gates is correct.""" dev = qml.device("default.gaussian", wires=2, hbar=hbar) r0, phi0, r1, phi1 = [0.4, -0.3, -0.7, 0.2] with qml.tape.JacobianTape() as tape: qml.Squeezing(r0, phi0, wires=[0]) qml.Squeezing(r1, phi1, wires=[0]) qml.expval(qml.NumberOperator(0)) # second order spy2 = mocker.spy(qml.gradients.parameter_shift_cv, "second_order_param_shift") tapes, fn = param_shift_cv(tape, dev, force_order2=True) grad_A2 = fn(dev.batch_execute(tapes)) spy2.assert_called() # check against the known analytic formula expected = np.zeros([4]) expected[0] = np.cosh(2 * r1) * np.sinh( 2 * r0) + np.cos(phi0 - phi1) * np.cosh(2 * r0) * np.sinh(2 * r1) expected[1] = -0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh( 2 * r1) expected[2] = np.cos(phi0 - phi1) * np.cosh(2 * r1) * np.sinh( 2 * r0) + np.cosh(2 * r0) * np.sinh(2 * r1) expected[3] = 0.5 * np.sin(phi0 - phi1) * np.sinh(2 * r0) * np.sinh( 2 * r1) assert np.allclose(grad_A2, expected, atol=tol, rtol=0)
def global_ground(weights): # Circuit gates full_circuit(weights) # Projector on the global ground state P = np.zeros((2**tot_qubits, 2**tot_qubits)) P[0, 0] = 1.0 return qml.expval(qml.Hermitian(P, wires=range(tot_qubits)))
def full_mask(self, mask_type: Type[Mask]) -> np.ndarray: """ Accumulated mask of layer, wire, and parameter masks for a given type of Mask. Note that this mask is readonly. """ try: result = self.masks[Axis.PARAMETERS][mask_type].mask.copy() except KeyError: result = np.zeros( self.parameters.shape, dtype=mask_type.dtype, requires_grad=False, ) shape = result.shape if Axis.WIRES in self.masks and mask_type in self.masks[Axis.WIRES]: wires = self.masks[Axis.WIRES][mask_type].mask # create new axes to match the shape to properly broadcast values result = (result + wires[(np.newaxis, slice(None), *[np.newaxis] * (len(shape) - 2))]) if Axis.LAYERS in self.masks and mask_type in self.masks[Axis.LAYERS]: layers = self.masks[Axis.LAYERS][mask_type].mask # create new axes to match the shape to properly broadcast values result = result + layers[(slice(None), *[np.newaxis] * (len(shape) - 1))] return result
def ancilla_ground(weights): # Circuit gates full_circuit(weights) # Projector on the ground state of the ancillary system P_anc = np.zeros((2**m, 2**m)) P_anc[0, 0] = 1.0 return qml.expval(qml.Hermitian(P_anc, wires=range(n_qubits, tot_qubits)))
def config(X): n = 2**int(np.ceil(np.log2(len(X[0])))) # len(X[0]) # número de qubits necessário para armazenar o dado codificado. # pylint: disable=no-member N = n # número total de qubits no circuito. w = 2 * n - 1 # número de parâmetros do circuito (weights) X = np.c_[X, np.zeros((len(X), n - len(X[0])))] # pylint: disable=no-member return n, N, w, X
def config(X): n = int(np.ceil(np.log2(len(X[0])))) # pylint: disable=no-member n = 2**int(np.ceil(np.log2(n))) # n tem que ser potência de 2. # pylint: disable=no-member N = 2**n - 1 # len(X[0])-1 # N precisa ser tal que n seja potência de 2 mais próxima de log_2(X[0]). O hierarchical exige que n seja dessa maneira. w = 2 * n - 1 # número de parâmetros do circuito (weights) X = np.c_[X, np.zeros((len(X), 2**n - len(X[0])))] # o número de qubits necessários para codificar os dados (log_2(N)) precisa ser uma potencia de 2. # pylint: disable=no-member return n, N, w, X
def config(X): n = int(np.ceil(np.log2(len(X[0])))) # pylint: disable=no-member n = 2**int(np.ceil(np.log2(n))) # n tem que ser potência de 2. # pylint: disable=no-member N = n # número total de qubits no circuito. w = 2*n - 1 # número de parâmetros do circuito (weights) X = np.c_[X, np.zeros((len(X), 2**n-len(X[0])))] # o número de qubits necessários para codificar os dados (log_2(N)) precisa ser uma potencia de 2. # pylint: disable=no-member return n, N, w, X
def test_single_qubit(self, n_layers): weights = np.zeros((n_layers, 1, 3)) with pennylane._queuing.OperationRecorder() as rec: StronglyEntanglingLayers(weights, wires=range(1)) assert len(rec.queue) == n_layers assert all([isinstance(q, qml.Rot) for q in rec.queue]) assert all([q._wires[0] == 0 for q in rec.queue])
def decimalToBinaryFixLength(_length, _decimal): binNum = bin(int(_decimal))[2:] outputNum = [int(item) for item in binNum] if len(outputNum) < _length: outputNum = np.concatenate((np.zeros((_length-len(outputNum),)),np.array(outputNum))) else: outputNum = np.array(outputNum) return outputNum
def test_reduced_row_echelon(binary_matrix, result): r"""Test that _reduced_row_echelon returns the correct result.""" # build row echelon form of the matrix shape = binary_matrix.shape for irow in range(shape[0]): pivot_index = 0 if np.count_nonzero(binary_matrix[irow, :]): pivot_index = np.nonzero(binary_matrix[irow, :])[0][0] for jrow in range(shape[0]): if jrow != irow and binary_matrix[jrow, pivot_index]: binary_matrix[jrow, :] = (binary_matrix[jrow, :] + binary_matrix[irow, :]) % 2 indices = [ irow for irow in range(shape[0] - 1) if np.array_equal(binary_matrix[irow, :], np.zeros(shape[1])) ] temp_row_echelon_matrix = binary_matrix.copy() for row in indices[::-1]: temp_row_echelon_matrix = np.delete(temp_row_echelon_matrix, row, axis=0) row_echelon_matrix = np.zeros(shape, dtype=int) row_echelon_matrix[:shape[0] - len(indices), :] = temp_row_echelon_matrix # build reduced row echelon form of the matrix from row echelon form for idx in range(len(row_echelon_matrix))[:0:-1]: nonzeros = np.nonzero(row_echelon_matrix[idx])[0] if len(nonzeros) > 0: redrow = (row_echelon_matrix[idx, :] % 2).reshape(1, -1) coeffs = ((-row_echelon_matrix[:idx, nonzeros[0]] / row_echelon_matrix[idx, nonzeros[0]]) % 2).reshape( 1, -1) row_echelon_matrix[:idx, :] = (row_echelon_matrix[:idx, :] + (coeffs.T * redrow) % 2) % 2 # get reduced row echelon form from the _reduced_row_echelon function rref_bin_mat = _reduced_row_echelon(binary_matrix) assert (rref_bin_mat == row_echelon_matrix).all() assert (rref_bin_mat == result).all()
def layer(theta, phi, wires): M = len(wires) phi_nonlinear = np.pi / 2 qml.templates.Interferometer( theta, phi, np.zeros(M), wires=wires, mesh="triangular", ) for i in wires: qml.Kerr(phi_nonlinear, wires=i)
def _train(self, n_epochs, batch_size, starting_weights, compute_cost): if starting_weights is None: weights = self.embedding.random_starting_weights() else: if np.shape(starting_weights) != self.embedding.weight_shape: raise ValueError( f'Starting weights must have shape {self.embedding.weight_shape}, was given {np.shape(starting_weights)}.' ) weights = starting_weights weights_history = np.zeros((n_epochs + 1, ) + self.embedding.weight_shape) weights_history[0] = weights if compute_cost: cost_history = np.zeros(n_epochs + 1) cost_history[0] = self.cost(weights, self.X_1, self.X_2) # expose the embedding 'n_epochs' times to the whole dataset for i in range(n_epochs): # iterate over batched data for input_batch, target_batch in self.__iterate_minibatches( batch_size): filter = target_batch == self.data_classes[0] inputs_1 = input_batch[filter] inputs_2 = input_batch[np.logical_not(filter)] weights = self.opt.step(self.cost, weights, inputs_1=inputs_1, inputs_2=inputs_2) weights_history[i + 1] = weights if compute_cost: cost_history[i + 1] = self.cost(weights, self.X_1, self.X_2) print(f'Cost after epoch {i + 1}: {cost_history[i]}') else: print(f'Completed epoch {i+1}') if compute_cost: return weights, (weights_history, cost_history) else: return weights, weights_history
def _accumulated_mask(self, for_differentiable=True) -> np.ndarray: result = np.zeros( shape=self.parameters.shape, dtype=bool if for_differentiable is True else float, ) for mask_type in { key for value in self.masks.values() for key in value if key.relevant_for_differentiation is for_differentiable }: result = result + self.full_mask(mask_type) return result
def two_waves_gen(n_samples): X = np.random.uniform(low=-np.pi, high=np.pi, size=(n_samples, 2)) y = np.zeros(n_samples, dtype=int) f = lambda x: np.sin(2 * x) + 1 g = lambda x: np.sin(3 * x + 1) - 1 for i in range(n_samples): if f(X[i, 0]) >= X[i, 1] >= g(X[i, 0]): y[i] = 1 return np.array(X, requires_grad=False), np.array(y, requires_grad=False)