def grad_one_source(s, p_warm_start, p_grad_warm_start, w, training_data, params):
	(A, A_data) = edge_computation.compute_A(w, training_data["feature_stack"], training_data["edge_ij"], params["edge_strength_fun"], training_data["num_nodes"])
	Q = edge_computation.compute_Q(A, A_data, training_data["edge_ij"], s, training_data["num_nodes"], params)
	Q_grad = []
	for k in range(training_data["num_features"]):
		(df_dwk, df_dwk_data) = edge_computation.compute_df_dwk(k, training_data["feature_stack"], A_data, training_data["edge_ij"], params["edge_strength_grad_fun"], training_data["num_nodes"], params)
		dQ_dwk = edge_computation.compute_dQ_dwk(k, df_dwk, df_dwk_data, A, params, training_data["edge_ij"], A_data)
		Q_grad.append(dQ_dwk)

	(p_grad, p) = partial_gradient_update.update_p_grad(p_warm_start, p_grad_warm_start, Q, Q_grad, training_data["num_features"], params)

	dpprime_dp = compute_dpprime_dp(training_data, p)

	(p_prime, sum_p_candidates) = compute_p_prime(training_data, p)

	dpprime_dw = dpprime_dp.dot(p_grad) #Note: dpprime_dw is dense, even though it might have lots of zeros
	diff_generating_mat = training_data["diff_generating_mat"]
	diffs = diff_generating_mat.dot(p_prime)
	dpprime_dw_diffs = diff_generating_mat.dot(dpprime_dw) #This is (|L||D|) x num_features
	dh_ddiffs = params["h_grad_fun"](diffs, params["margin"])

	#print(dpprime_dw_diffs)

	dh_dw = numpy.dot(dpprime_dw_diffs.T, dh_ddiffs)

	#print(dh_dw)
	
	return (dh_dw, p, p_grad)
import numpy.random
import scipy.sparse
import partial_gradient_update
import page_rank_update
import sys
import random

num_nodes = 2
perturbation = 0
rho = float(sys.argv[1]) * random.random()

numpy.random.seed(0)
Q_dense = numpy.random.rand(num_nodes, num_nodes)
Q_dense /= numpy.tile(numpy.dot(Q_dense, numpy.ones((num_nodes, 1))), (1, num_nodes))
print(numpy.eye(num_nodes, num_nodes) - Q_dense.T)
A = numpy.eye(num_nodes, num_nodes) - Q_dense.T
z = 100.0 * numpy.random.rand(num_nodes, 1)
Q_grad_dense = numpy.tile(numpy.dot(A, z), (1, num_nodes)).T
Q_grad_dense += perturbation * numpy.random.rand(num_nodes, num_nodes)
Q = scipy.sparse.csr_matrix(Q_dense)
p = numpy.random.rand(num_nodes, 1)
p_grad = numpy.zeros((num_nodes, 1))
p_grad[0] = rho
p /= numpy.sum(p)
p = page_rank_update.update_p(p, Q, {"page_rank_epsilon": 1e-12})
print(p)
Q_grad = scipy.sparse.csr_matrix(Q_grad_dense)
print(Q_grad.T.dot(p))
p_grad = partial_gradient_update.update_p_grad(p, p_grad, Q, [Q_grad], 1, {"partial_gradient_update_epsilon": 1e-12})
print(numpy.dot(A, p_grad) - numpy.dot(A, z))