def saveJacobian(save_filename, n_el=20, n_per_el=3):
    # number electrodes
    el_pos = np.arange(n_el * n_per_el)
    # create an object with the meshing characteristics to initialise a Forward object
    mesh_obj = mesh(n_el)
    fwd = Forward(mesh_obj, el_pos, n_el)
    ex_mat = train.generateExMat(ne=n_el)
    f, meas, new_ind = fwd.solve_eit(ex_mat=ex_mat, perm=fwd.tri_perm)
    
    #print(f)
    ind = np.arange(len(meas))
    np.random.shuffle(ind)
    pde_result = train.namedtuple("pde_result", ['jac', 'v', 'b_matrix'])

    f = pde_result(jac=f.jac[ind], v=f.v[ind], b_matrix=f.b_matrix[ind])
    meas = meas[ind]
    new_ind = new_ind[ind]
    h = h5.File(save_filename, 'w')

    try:
        h.create_dataset('jac', data=f.jac)
        h.create_dataset('v', data=f.v)
        h.create_dataset('b', data=f.b_matrix)
        h.create_dataset('meas', data=meas)
        h.create_dataset('new_ind', data=new_ind)
        h.create_dataset('p', data=mesh_obj['node'])
        h.create_dataset('t', data=mesh_obj['element'])
    except:
        TypeError('Error with saving files!')
    h.close()
def getNextPrediction(fileJac: str, measuring_electrodes: np.ndarray, voltages: np.ndarray, 
              num_returned: int=10, n_el: int=20, n_per_el: int=3, n_pix: int=64, pert: float=0.5, 
              p_influence: float=-10., p_rec: float=10., p: float=0.2, lamb:float=0.1) -> np.ndarray:
    # extract const permittivity jacobian and voltage (& other)
    file = h5.File(fileJac, 'r')

    meas = file['meas'][()]
    new_ind = file['new_ind'][()]
    p = file['p'][()]
    t = file['t'][()]
    file.close()
    # initialise const permitivity and el_pos variables
    perm = np.ones(t.shape[0], dtype=np.float32)
    el_pos = np.arange(n_el * n_per_el).astype(np.int16)
    mesh_obj = {'element': t,
        'node':    p,
        'perm':    perm}
    # list all possible active/measuring electrode permutations of this measurement
    meas = cp.array(meas)
    # find their indices in the already calculated const. permitivity Jacobian (CPJ)
    measuring_electrodes = cp.array(measuring_electrodes)
    measurements_0 = cp.amin(measuring_electrodes[:, :2], axis=1)
    measurements_1 = cp.amax(measuring_electrodes[:, :2], axis=1)
    measurements_2 = cp.amin(measuring_electrodes[:, 2:], axis=1)
    measurements_3 = cp.amax(measuring_electrodes[:, 2:], axis=1)
    measuring_electrodes = cp.empty((len(measuring_electrodes), 4))
    measuring_electrodes[:, 0] = measurements_0
    measuring_electrodes[:, 1] = measurements_1
    measuring_electrodes[:, 2] = measurements_2
    measuring_electrodes[:, 3] = measurements_3
    index = (cp.sum(cp.equal(measuring_electrodes[:, None, :], meas[None, :, :]), axis=2) == 4)
    index = cp.where(index)
    #print(index)
    ind = cp.unique(index[1])
    #print(ind)
    i = cp.asnumpy(ind)
    j = index[0]
    mask = np.zeros(len(meas), dtype=int)
    mask[i] = 1
    mask = mask.astype(bool)
    # take a slice of Jacobian, voltage readings and B matrix (the one corresponding to the performed measurements)
    file = h5.File(fileJac, 'r')
    jac = file['jac'][mask, :][()]
    v = file['v'][mask][()]
    b = file['b'][mask, :][()]
    file.close()
    # put them in the form desired by the GREIT function
    pde_result = train.namedtuple("pde_result", ['jac', 'v', 'b_matrix'])
    f = pde_result(jac=jac,
           v=v,
           b_matrix=b)
    
    # now we can use the real voltage readings and the GREIT algorithm to reconstruct
    greit = train.greit.GREIT(mesh_obj, el_pos, f=f, ex_mat=(meas[index[1], :2]), step=None)
    greit.setup(p=p, lamb=lamb, n=n_pix)
    h_mat = greit.H
    reconstruction = greit.solve(voltages, f.v).reshape(n_pix, n_pix)
    # fix_electrodes_multiple is in meshing.py
    _, el_coords = train.fix_electrodes_multiple(centre=None, edgeX=0.1, edgeY=0.1, a=2, b=2, ppl=n_el, el_width=0.02, num_per_el=3)
    # find the distances between each existing electrode pair and the pixels lying on the liine that connects them
    pixel_indices, voltage_all_possible = measopt.find_all_distances(reconstruction, h_mat, el_coords, n_el, cutoff=0.8)
    # call function get_total_map that generates the influence map, the gradient map and the log-reconstruction
    total_map, grad_mat, rec_log = np.abs(measopt.get_total_map(reconstruction, voltages, h_mat, pert=pert, p_influence=p_influence, p_rec=p_rec))
    # get the indices of the total map along the lines connecting each possible electrode pair
    total_maps_along_lines = total_map[None] * pixel_indices
    # find how close each connecting line passes to the boundary of an anomaly (where gradient supposed to be higher)
    proximity_to_boundary = np.sum(total_maps_along_lines, axis=(1, 2)) / np.sum(pixel_indices, axis=(1, 2))
    # rate the possible src-sink pairs by their proximity to existing anomalies
    proposed_ex_line = voltage_all_possible[np.argsort(proximity_to_boundary)[::-1]][:num_returned]

    number_of_voltages = 10
    # generate the voltage measuring electrodes for this current driver pair
    proposed_voltage_pairs = measopt.findNextVoltagePair(proposed_ex_line[0], fileJac, total_map, number_of_voltages, 0, npix=n_pix, cutoff=0.97)
    return proposed_ex_line, proposed_voltage_pairs, reconstruction, total_map
def simulateMeasurements(fileJac, anomaly=0, measurements=None, v_meas=None, n_el=20, n_per_el=3, n_pix=64, a=2.):
	# extract const permittivity jacobian and voltage (& other)
	file = h5.File(fileJac, 'r')

	meas = file['meas'][()]
	new_ind = file['new_ind'][()]
	p = file['p'][()]
	t = file['t'][()]
	file.close()
	# initialise const permitivity and el_pos variables
	perm = np.ones(t.shape[0], dtype=np.float32)
	el_pos = np.arange(n_el * n_per_el).astype(np.int16)
	mesh_obj = {'element': t,
				'node':	p,
				'perm':	perm}

	#for testing
	if measurements is None:
		el_dist = np.random.randint(1, 20)
		ex_mat = (cp.concatenate((cp.arange(20)[None], (cp.arange(20) + el_dist)[None])) % 20).T
		#print(ex_mat.shape)
		fem_all = Forward(mesh_obj, el_pos)
		measurements = fem_all.voltMeter(ex_mat)
		#ex_mat = mesurements[1]
		measurements = cp.concatenate((measurements[1], measurements[0]), axis=1)
		#print(measurements.shape)
	# list all possible active/measuring electrode permutations of this measurement
	meas = cp.array(meas)
	# find their indices in the already calculated const. permitivity Jacobian (CPJ)
	measurements = cp.array(measurements)
	measurements_0 = cp.amin(measurements[:, :2], axis=1)
	measurements_1 = cp.amax(measurements[:, :2], axis=1)
	measurements_2 = cp.amin(measurements[:, 2:], axis=1)
	measurements_3 = cp.amax(measurements[:, 2:], axis=1)
	measurements = cp.empty((len(measurements), 4))
	measurements[:, 0] = measurements_0
	measurements[:, 1] = measurements_1
	measurements[:, 2] = measurements_2
	measurements[:, 3] = measurements_3
	index = (cp.sum(cp.equal(measurements[:, None, :], meas[None, :, :]), axis=2) == 4)
	index = cp.where(index)
	ind = cp.unique(index[1])
	i = cp.asnumpy(ind)
	j = index[0]
	mask = np.zeros(len(meas), dtype=int)
	mask[i] = 1
	mask = mask.astype(bool)
	# take a slice of Jacobian, voltage readings and B matrix
	file = h5.File(fileJac, 'r')
	jac = file['jac'][mask, :][()]
	v = file['v'][mask][()]
	b = file['b'][mask, :][()]
	file.close()
	pde_result = train.namedtuple("pde_result", ['jac', 'v', 'b_matrix'])
	f = pde_result(jac=jac,
				   v=v,
				   b_matrix=b)
	
	# simulate voltage readings if not given
	if v_meas is None:
		if np.isscalar(anomaly):
			print("generating new anomaly")
			anomaly = train.generate_anoms(a, a)
		true = train.generate_examplary_output(a, int(n_pix), anomaly)
		mesh_new = train.set_perm(mesh_obj, anomaly=anomaly, background=1)
		fem = FEM(mesh_obj, el_pos, n_el)
		new_ind = cp.array(new_ind)
		f2, raw = fem.solve_eit(volt_mat_all=meas[ind, 2:], new_ind=new_ind[ind], ex_mat=meas[ind, :2], parser=None, perm=mesh_new['perm'].astype('f8'))
		v_meas = f2.v
		'''
		#plot
		fig = plt.figure(3)
		x, y = p[:, 0], p[:, 1]
		ax1 = fig.add_subplot(111)
		# draw equi-potential lines
		print(raw.shape)
		raw = cp.asnumpy(raw[5]).ravel()
		vf = np.linspace(min(raw), max(raw), 32)
		ax1.tricontour(x, y, t, raw, vf, cmap=plt.cm.viridis)
		# draw mesh structure
		ax1.tripcolor(x, y, t, np.real(perm),
					  edgecolors='k', shading='flat', alpha=0.5,
					  cmap=plt.cm.Greys)

		ax1.plot(x[el_pos], y[el_pos], 'ro')
		for i, e in enumerate(el_pos):
			ax1.text(x[e], y[e], str(i+1), size=12)
		ax1.set_title('Equipotential Lines of Uniform Permittivity')
		# clean up
		ax1.set_aspect('equal')
		ax1.set_ylim([-1.2, 1.2])
		ax1.set_xlim([-1.2, 1.2])
		fig.set_size_inches(6, 6)
		#plt.show()'''
	elif len(measurements) == len(v_meas):
		measurements = np.array(measurements)
		v_meas = np.array(v_meas[j[:len(ind)]])
	else:
		raise ValueError('Sizes of arrays do not match (have to have voltage reading for each measurement). If you don\'t have readings, leave empty for simulation.')
	print('Number of measurements:', len(v_meas), len(f.v))

	# now we can use the real voltage readings and the GREIT algorithm to reconstruct
	greit = train.greit.GREIT(mesh_obj, el_pos, f=f, ex_mat=(meas[index[1], :2]), step=None)
	greit.setup(p=0.2, lamb=0.01, n=n_pix)
	h_mat = greit.H
	reconstruction = greit.solve(v_meas, f.v).reshape(n_pix, n_pix)
	
	# optional: see reconstruction
	'''
	plt.figure(1)
	im1 = plt.imshow(reconstruction, cmap=plt.cm.viridis, origin='lower', extent=[-1, 1, -1, 1])
	plt.title("Reconstruction")
	plt.colorbar(im1)
	plt.figure(2)
	im2 = plt.imshow(true, cmap=plt.cm.viridis, origin='lower', extent=[-1, 1, -1, 1])
	plt.colorbar(im2)
	plt.title("True Image")
	plt.show()
	'''
	return reconstruction, h_mat, v_meas, f.v, true, len(v_meas)