def rsh_transform_unit_test(max_degree=3, grid=sg.t_design5200()): l, m = zip(*lm_generator(max_degree)) Y = real_sph_harm_transform(l, m, grid.az, grid.el) # compute the grammian G = grid.w * Y @ Y.T.conj() # should be 1 on the diagonal, 0 off. max_error = np.max(np.abs(G - np.identity(G.shape[0]))) return max_error < 1e-8, max_error, G
def allrad(degree, order, speakers_azimuth, speakers_elevation, speaker_is_real=None, v_az=None, v_el=None, vbap_norm=True): """ Compute basic decoder matrix by the AllRAD method. Parameters ---------- degree : TYPE DESCRIPTION. order : TYPE DESCRIPTION. speakers_azimuth : TYPE DESCRIPTION. speakers_elevation : TYPE DESCRIPTION. speaker_is_real : TYPE, optional DESCRIPTION. The default is None. v_az : TYPE, optional DESCRIPTION. The default is None. v_el : TYPE, optional DESCRIPTION. The default is None. vbap_norm : TYPE, optional DESCRIPTION. The default is True. Returns ------- M : TYPE DESCRIPTION. """ # defaults if v_az is None: td = sg.t_design5200() v_az = td.az v_el = td.el Su = np.array(sg.sph2cart(speakers_azimuth, speakers_elevation)) Vu = np.array(sg.sph2cart(v_az, v_el)) V2R, Vtri, Vxyz = allrad_v2rp(Su, Vu, vbap_norm=vbap_norm) Mv = inversion(degree, order, v_az, v_el) M = np.matmul(V2R, Mv) if speaker_is_real is not None: # get rid of rows corresponding to imaginary speakers M = M[speaker_is_real] return M
def allrad2(degree, order, spkrs_az, spkrs_el, v_az=None, v_el=None, vbap_norm=True): """ Compute decoder by AllRAD2 method. Not implemented. Parameters ---------- degree : TYPE DESCRIPTION. order : TYPE DESCRIPTION. spkrs_az : TYPE DESCRIPTION. spkrs_el : TYPE DESCRIPTION. v_az : TYPE, optional DESCRIPTION. The default is None. v_el : TYPE, optional DESCRIPTION. The default is None. vbap_norm : TYPE, optional DESCRIPTION. The default is True. Returns ------- M_allrad : TYPE DESCRIPTION. """ # defaults if v_az is None: td = sg.t_design5200() v_az = td.az v_el = td.el # TODO understand and implement allrad2 :) :) M_allrad = allrad(degree, order, spkrs_az, spkrs_el, v_az=v_az, v_el=v_el, vbap_norm=vbap_norm) # TODO rest of AllRAD2 method return M_allrad
def real_sph_harm_inner_product_discrete(l1, m1, l2, m2, grid=sg.t_design5200()): if True: Y1 = real_sph_harm_transform(l1, m1, grid.az, grid.el) Y2 = real_sph_harm_transform(l2, m2, grid.az, grid.el) s = (grid.w * Y1 @ Y2.T)[0, 0] else: # take advantage of broadcasting Y = real_sph_harm_transform((l1, l2), (m1, m2), grid.az, grid.el) s = grid.w * Y[0, :] @ Y[1, :].T return s
def optimize_dome_LF(M_hf, S, ambisonic_order=3, el_lim=-π/8): order_h, order_v, sh_l, sh_m, id_string = pc.ambisonic_channels(ambisonic_order) # the test directions T = sg.t_design5200() cap = sg.spherical_cap(T.u, (0, 0, 1), 5*np.pi/6)[0] W = np.where(cap, 0.1, 1) M_lf, res = optimize_LF(M_hf, S.u.T, sh_l, sh_m, W) return M_lf, res
def optimize_dome(S, # the speaker array ambisonic_order=3, el_lim=-π/8, tikhonov_lambda=1e-3, sparseness_penalty=1, do_report=False, rE_goal='auto', eval_order=None, random_start=False ): """Test optimizer with CCRMA Stage array.""" # # order_h, order_v, sh_l, sh_m, id_string = pc.ambisonic_channels(ambisonic_order) order = max(order_h, order_v) # FIXME is_3D = order_v > 0 if eval_order is None: eval_order = ambisonic_order eval_order_given = False else: eval_order_given = True eval_order_h, eval_order_v, eval_sh_l, eval_sh_m, eval_id_string = \ pc.ambisonic_channels(eval_order) mask_matrix = pc.mask_matrix(eval_sh_l, eval_sh_m, sh_l, sh_m) print(mask_matrix) # if True: # S = esa.stage2017() + esa.nadir()#stage() # spkr_array_name = S.name # else: # # hack to enter Eric's array # S = emb() # spkr_array_name = 'EMB' spkr_array_name = S.name print('speaker array = ', spkr_array_name) S_u = np.array(sg.sph2cart(S.az, S.el, 1)) gamma = shelf.gamma(sh_l, decoder_type='max_rE', decoder_3d=is_3D, return_matrix=True) figs = [] if not random_start: M_start = 'AllRAD' M_allrad = bd.allrad(sh_l, sh_m, S.az, S.el, speaker_is_real=S.is_real) # remove imaginary speaker from S_u and Sr S_u = S_u[:, S.is_real] #S.Real.values] Sr = S[S.is_real] #S.Real.values] M_allrad_hf = M_allrad @ gamma # performance plots plot_title = "AllRAD, " if eval_order_given: plot_title += f"Design: {id_string}, Test: {eval_id_string}" else: plot_title += f"Signal set={id_string}" figs.append( lm.plot_performance(M_allrad_hf, S_u, sh_l, sh_m, mask_matrix = mask_matrix, title=plot_title)) lm.plot_matrix(M_allrad_hf, title=plot_title) print(f"\n\n{plot_title}\nDiffuse field gain of each loudspeaker (dB)") for n, g in zip(Sr.ids, 10 * np.log10(np.sum(M_allrad ** 2, axis=1))): print(f"{n:3}:{g:8.2f} |{'=' * int(60 + g)}") else: M_start = 'Random' # let optimizer dream up a decoder on its own M_allrad = None # more mess from imaginary speakers S_u = S_u[:, S.is_real] Sr = S[S.is_real] # M_allrad = None # Objective for E T = sg.t_design5200() cap, *_ = sg.spherical_cap(T.u, (0, 0, 1), # apex π/2 - el_lim) E0 = np.where(cap, 1.0, 0.1) # inside, outside # np.array([0.1, 1.0])[cap.astype(np.int8)] # Objective for rE order+2 inside the cap, order-2 outside rE_goal = np.where(cap, shelf.max_rE_3d(order+2), # inside the cap shelf.max_rE_3d(max(order-2, 1)) # outside the cap ) #np.array([shelf.max_rE_3d(max(order-2, 1)), # shelf.max_rE_3d(order+2)])[cap.astype(np.int8)] M_opt, res = optimize(M_allrad, S_u, sh_l, sh_m, E_goal=E0, iprint=50, tikhonov_lambda=tikhonov_lambda, sparseness_penalty=sparseness_penalty, rE_goal=rE_goal) plot_title = f"Optimized {M_start}, " if eval_order_given: plot_title += f"Design: {id_string}, Test: {eval_id_string}" else: plot_title += f"Signal set={id_string}" figs.append( lm.plot_performance(M_opt, S_u, sh_l, sh_m, mask_matrix = mask_matrix, title=plot_title )) lm.plot_matrix(M_opt, title=plot_title) with io.StringIO() as f: print(f"ambisonic_order = {order}\n" + f"el_lim = {el_lim * 180 / π}\n" + f"tikhonov_lambda = {tikhonov_lambda}\n" + f"sparseness_penalty = {sparseness_penalty}\n", file=f) off = np.isclose(np.sum(M_opt ** 2, axis=1), 0, rtol=1e-6) # 60dB down print("Using:\n", Sr.ids[~off.copy()], file=f) print("Turned off:\n", Sr.ids[off.copy()], file=f) print("\n\nDiffuse field gain of each loudspeaker (dB)", file=f) for n, g in zip(Sr.ids, 10 * np.log10(np.sum(M_opt ** 2, axis=1))): print(f"{n:3}:{g:8.2f} |{'=' * int(60 + g)}", file=f) report = f.getvalue() print(report) if do_report: reports.html_report(zip(*figs), text=report, directory=spkr_array_name, name=f"{spkr_array_name}-{id_string}") return M_opt, dict(M_allrad=M_allrad, off=off, res=res)
s_el = (0, 0, 0, 0, pi / 2, -pi / 2) else: s = sg.t_design() s_az = s.az s_el = s.el Su = np.array(sg.sph2cart(s_az, s_el)) tri = Delaunay(Su.transpose()) H = tri.convex_hull # assemble the list of face vertices p0 = tri.points[H[:, 0], :] p1 = tri.points[H[:, 1], :] p2 = tri.points[H[:, 2], :] V = sg.t_design5200() origin = np.array([0, 0, 0]) a = [] Hr = np.arange(len(H)) for i in range(5200): flag, u, v, t = rti_fun(origin, V.u[:, i], p0, p1, p2) valid = np.logical_and(flag, t > 0) face = Hr[valid][0] ur = u[valid][0] vr = v[valid][0] tr = t[valid][0] a.append((face, ur, vr, tr)) return a
def optimize_LF(M, Su, sh_l, sh_m, W=1, raise_error_on_failure=False): M_shape = M.shape g_spkr, g_total = lm.diffuse_field_gain(M) # the test directions T = sg.t_design5200() cap = sg.spherical_cap(T.u, (0, 0, 1), 5 * np.pi / 6)[0] W = np.where(cap, 1, 1) Y_test = rsh.real_sph_harm_transform(sh_l, sh_m, T.az, T.el) rExyz, E = rE(M, Su, Y_test) rEu, rEr = xyz2ur(rExyz) # define the loss function def o(x): M = x.reshape(M_shape) rVxyz, P = rV(M, Su, Y_test) df_gain = np.sum(M * M) df_gain_loss = (g_total - df_gain)**2 # Tikhonov regularization term - typical value = 1e-3 tikhonov_regularization_term = np.sum(M**2) * 1e-2 #tikhonov_lambda # dir loss mag(rVxyz) should be 1 direction_loss = np.sum(W * ((rVxyz - rEu)**2)) P_loss = np.sum(W * ((P - 1)**2)) return (direction_loss + df_gain_loss + P_loss / 100000 + tikhonov_regularization_term) val_and_grad_fn = jax.value_and_grad(o) val_and_grad_fn = jax.jit(val_and_grad_fn) def objective_and_gradient(x): v, g = val_and_grad_fn(x) g = onp.array(g, order='F') return v, g x0 = M.ravel() with Timer() as t: res = opt.minimize( objective_and_gradient, x0, bounds=opt.Bounds(-1, 1), method='L-BFGS-B', jac=True, options=dict(disp=50, # maxls=50, # maxcor=30, # gtol=1e-8, # ftol=1e-12 ), # callback=callback, ) if True: print() print(f"Execution time: {t.interval:0.3f} sec.") print(res.message) print(res) print() if res.status != 0 and raise_error_on_failure: print('bummer:', res.message) raise RuntimeError(res.message) M_opt = res.x.reshape(M_shape) return M_opt, res
def optimize( M, Su, sh_l, sh_m, E_goal=None, iprint=50, tikhonov_lambda=1.0e-3, sparseness_penalty=1, uniform_loudness_penalty=0.1, #0.01 rE_goal=1.0, maxcor=100, # how accurate is the Hessian, more is better but slower raise_error_on_failure=True): """Optimize psychoacoustic criteria.""" # # handle defaults if E_goal is None: E_goal = 1 if rE_goal == 'auto' or rE_goal is None: #FIXME This assumes 3D arrays rE_goal = shelf.max_rE_3d(np.max(sh_l) + 2) print(f"rE_goal min={np.min(rE_goal)} max={np.max(rE_goal)}") # the test directions T = sg.t_design5200() Y_test = rsh.real_sph_harm_transform(sh_l, sh_m, T.az, T.el) if M is None: # infer M_shape from Su and Y M_shape = ( Su.shape[1], # number of loudspeakers Y_test.shape[0], # number of program channels ) M = random.uniform(key, shape=M_shape, minval=-1.0, maxval=1.0) else: M_shape = M.shape # the loss function def o(x) -> float: M = x.reshape(M_shape) rExyz, E = rE(M, Su, Y_test) # truncation loss due to finite order truncation_loss = np.sum((rExyz - T.u * rE_goal)**2) # uniform loudness loss uniform_loudness_loss = (np.sum( (E - E_goal)**2) * uniform_loudness_penalty) # was 10 # Tikhonov regularization term - typical value = 1e-3 tikhonov_regularization_term = np.sum(M**2) * tikhonov_lambda # don't turn off speakers # pull diffuse-field gain for each speaker away from zero sparsness_term = (np.sum(np.abs(1 - np.sum(M**2, axis=1))) * 100 * sparseness_penalty) # the entire loss function f = (truncation_loss + uniform_loudness_loss + tikhonov_regularization_term + sparsness_term) return f # consult the automatic differentiation oracle val_and_grad_fn = jax.value_and_grad(o) val_and_grad_fn = jax.jit(val_and_grad_fn) def objective_and_gradient(x, *args): v, g = val_and_grad_fn(x, *args) # I'm not to happy about having to copy g but L-BGFS needs it in # Fortran order and JAX only does C order. Check with g.flags # NOTE: asarray() and asfortranarray() don't work correctly here g = onp.array(g, order='F') return v, g x0 = M.ravel() # initial guess with Timer() as t: # https://docs.scipy.org/doc/scipy/reference/optimize.minimize-lbfgsb.html res = opt.minimize( objective_and_gradient, x0, bounds=opt.Bounds(-1, 1), method='L-BFGS-B', jac=True, options=dict( maxcor=maxcor, disp=iprint, maxls=500, # maxcor=30, gtol=1e-8, #ftol=1e-12 ), # callback=callback, ) if True: print() print(f"Execution time: {t.interval:0.3f} sec.") print(res.message) print(res) print() if res.status != 0 and raise_error_on_failure: print('bummer:', res.message) raise RuntimeError(res.message) M_opt = res.x.reshape(M_shape) return M_opt, res