//  [python]
def compute_loss(self, probs, predictions, generate_targets,
					 source_copy_targets, source_dynamic_vocab_size,
					 target_copy_targets, target_dynamic_vocab_size,
					 coverage_records, copy_attentions, debug=False):
		"""
		Priority: target_copy > source_copy > generate

		:param probs: probability distribution,
			[batch_size, num_target_nodes, vocab_size + dynamic_vocab_size]
		:param predictions: [batch_size, num_target_nodes]
		:param generate_targets: target node index in the vocabulary,
			[batch_size, num_target_nodes]
		:param source_copy_targets:  target node index in the dynamic vocabulary,
			[batch_size, num_target_nodes]
		:param source_dynamic_vocab_size: int
		:param target_copy_targets:  target node index in the dynamic vocabulary,
			[batch_size, num_target_nodes]
		:param target_dynamic_vocab_size: int
		:param coverage_records: None or a tensor recording source-side coverages.
			[batch_size, num_target_nodes, num_source_nodes]
		:param copy_attentions: [batch_size, num_target_nodes, num_source_nodes]
		"""
		# pdb.set_trace()
		generate_targets=generate_targets[:,1:]
		non_pad_mask = tf.cast(tf.not_equal(generate_targets,self.vocab_pad_idx),tf.int32)

		#source_copy_mask = source_copy_targets.ne(1) & source_copy_targets.ne(0)  # 1 is the index for unknown words
		source_copy_mask = tf.cast(tf.logical_and(tf.not_equal(source_copy_targets,1),tf.not_equal(source_copy_targets,0)),tf.int32)
		non_source_copy_mask = 1 - source_copy_mask

		target_copy_mask = tf.cast(tf.not_equal(target_copy_targets,0),tf.int32)
		non_target_copy_mask = 1 - target_copy_mask

		# [batch_size, num_target_nodes, 1]
		target_copy_targets_with_offset = tf.expand_dims(target_copy_targets,2) + self.vocab_size + source_dynamic_vocab_size
		#=================================================
		# [batch_size, num_target_nodes]
		target_copy_target_probs = tf.squeeze(tf.batch_gather(probs,target_copy_targets_with_offset),2)
		target_copy_target_probs = target_copy_target_probs*tf.cast(target_copy_mask,dtype=tf.float32)
		# target_copy_target_probs = probs.gather(dim=2, index=target_copy_targets_with_offset).squeeze(2)
		# target_copy_target_probs = target_copy_target_probs.mul(target_copy_mask.float())

		# [batch_size, num_target_nodes, 1]
		source_copy_targets_with_offset = tf.expand_dims(source_copy_targets,2) + self.vocab_size
		# [batch_size, num_target_nodes]
		source_copy_target_probs = tf.squeeze(tf.batch_gather(probs,source_copy_targets_with_offset),2)
		source_copy_target_probs = source_copy_target_probs*tf.cast(non_target_copy_mask,dtype=tf.float32)*tf.cast(source_copy_mask,dtype=tf.float32)

		# [batch_size, num_target_nodes]
		generate_target_probs = tf.squeeze(tf.batch_gather(probs,generate_targets[:,:,None]),2)

		# Except copy-oov nodes, all other nodes should be copied.
		likelihood = target_copy_target_probs + source_copy_target_probs + \
					 generate_target_probs * tf.cast(non_target_copy_mask,dtype=tf.float32) * tf.cast(non_source_copy_mask,dtype=tf.float32)
		num_tokens = tf.reduce_sum(tf.cast(non_pad_mask,tf.int32))

		if not self.force_copy:
			non_generate_oov_mask = tf.not_equal(generate_targets,1)
			additional_generate_mask = (non_target_copy_mask & source_copy_mask & non_generate_oov_mask)
			likelihood = likelihood + generate_target_probs * tf.cast(additional_generate_mask,dtype=tf.float32)
			num_tokens += tf.reduce_sum(tf.cast(additional_generate_mask,tf.float32))

		# Add eps for numerical stability.
		likelihood = likelihood + self.eps

		coverage_loss = 0
		if coverage_records is not None:
			coverage_loss = tf.reduce_sum(tf.reduce_min(coverage_records, copy_attentions), 2) * tf.cast(non_pad_mask, dtype=tf.float32)

		# Drop pads.
		loss = -tf.log(likelihood) * tf.cast(non_pad_mask, dtype=tf.float32) + coverage_loss

		# Mask out copy targets for which copy does not happen.
		targets = tf.squeeze(target_copy_targets_with_offset,2) * target_copy_mask + \
				  tf.squeeze(source_copy_targets_with_offset,2) * non_target_copy_mask * source_copy_mask + \
				  generate_targets * non_target_copy_mask * non_source_copy_mask
		targets = targets * non_pad_mask

		pred_eq = tf.cast(tf.equal(predictions,targets),tf.int32) * non_pad_mask

		num_non_pad = tf.reduce_sum(non_pad_mask)
		num_correct_pred = tf.reduce_sum(pred_eq)

		num_target_copy = tf.reduce_sum(target_copy_mask * non_pad_mask)
		num_correct_target_copy = tf.reduce_sum(pred_eq * target_copy_mask)
		num_correct_target_point = tf.reduce_sum(tf.cast(tf.greater_equal(predictions,self.vocab_size + source_dynamic_vocab_size),tf.int32)*target_copy_mask*non_pad_mask)

		num_source_copy = tf.reduce_sum(source_copy_mask * non_target_copy_mask * non_pad_mask)
		num_correct_source_copy = tf.reduce_sum(pred_eq * non_target_copy_mask * source_copy_mask)
		num_correct_source_point = tf.reduce_sum(tf.cast(tf.greater_equal(predictions,self.vocab_size), tf.int32) * tf.cast(tf.less(predictions,self.vocab_size + source_dynamic_vocab_size), tf.int32) * non_target_copy_mask * source_copy_mask * non_pad_mask)

		# self.metrics(loss.sum().item(), num_non_pad, num_correct_pred,
		#              num_source_copy, num_correct_source_copy, num_correct_source_point,
		#              num_target_copy, num_correct_target_copy, num_correct_target_point
		#              )
		# num_correct_sequences = pred_eq = tf.cast(tf.equal(predictions,targets),tf.int32)
		# correct_tokens = nn.equal(targets, predictions) * token_weights
		# pdb.set_trace()
		# (n x m) -> (n)
		tokens_per_sequence = tf.reduce_sum(non_pad_mask, axis=-1)
		# (n x m) -> (n)
		correct_tokens_per_sequence = tf.reduce_sum(pred_eq, axis=-1)
		# (n), (n) -> (n)
		correct_sequences = tf.cast(nn.equal(tokens_per_sequence, correct_tokens_per_sequence),tf.float32)
		outputs=dict(
			loss=tf.reduce_sum(loss)/tf.cast(num_tokens,dtype=tf.float32),
			total_loss=tf.reduce_sum(loss),
			n_tokens=num_tokens,
			predictions=predictions,
			n_correct_tokens=num_correct_pred,
			n_correct_sequences=tf.reduce_sum(correct_sequences)
		)
		if debug:
			outputs['pred_eq']=pred_eq
			outputs['targets']=targets
			outputs['target_copy_targets_with_offset']=target_copy_targets_with_offset
			outputs['target_copy_mask']=target_copy_mask
			outputs['source_copy_targets_with_offset']=source_copy_targets_with_offset
			outputs['non_target_copy_mask']=non_target_copy_mask
			outputs['source_copy_mask']=source_copy_mask
			outputs['generate_targets']=generate_targets
			outputs['num_correct_pred']=num_correct_pred
			outputs['correct_tokens_per_sequence']=correct_tokens_per_sequence
		return outputs