eddy_em: (Костерок)
[personal profile] eddy_em
Таки добрался я наконец до своих баранов: вернулся к полиномам Цернике. И что оказалось: ведь отлично справляется метод наименьших квадратов! Т.е. в принципе, наверное, мучиться с ортогональными на кольце полиномами и не нужно!!!

Итак, вот собственно код, занимающийся поиском коэффициентов разложения «волнового фронта» по полиномам Цернике методом наименьших квадратов на любом наборе пробных точек:
double *LS_decompose(int Nmax, int Sz, polar *P, double *heights, int *Zsz, int *lastIdx){
	int pmax = (Nmax + 1) * (Nmax + 2) / 2; // max Z number in Noll notation
/*
	typedef struct	{
		size_t size1;		// rows (height)
		size_t size2;		// columns (width)
		size_t tda;			// data width (aligned) - data stride
		double * data;		// pointer to block->data (matrix data itself)
		gsl_block * block;	// block with matrix data (block->size is size)
		int owner;			// ==1 if matrix owns 'block' (then block will be freed with matrix)
	} gsl_matrix;
*/
	// Now allocate matrix: its Nth row is equation for Nth data point,
	// Mth column is Z_M
	gsl_matrix *M = gsl_matrix_calloc(Sz, pmax);
	// fill matrix with coefficients
	int x,y;
	size_t T = M->tda;
	for(x = 0; x < pmax; x++){
		double norm, *Zcoeff = zernfunNR(x, Sz, P, &norm), *Zptr = Zcoeff;
		double *ptr = &(M->data[x]);
		for(y = 0; y < Sz; y++, ptr+=T, Zptr++) // fill xth polynomial
			*ptr = (*Zptr);// / norm;
		FREE(Zcoeff);
	}

	gsl_vector *ans = gsl_vector_calloc(pmax);

	gsl_vector_view b = gsl_vector_view_array(heights, Sz);
	gsl_vector *tau = gsl_vector_calloc(pmax); // min(size(M))
	gsl_linalg_QR_decomp(M, tau);

	gsl_vector *residual = gsl_vector_calloc(Sz);
	gsl_linalg_QR_lssolve(M, tau, &b.vector, ans, residual);

	double *ptr = ans->data;
	int maxIdx = 0;
	double *Zidxs = MALLOC(double, pmax);
	for(x = 0; x < pmax; x++, ptr++){
		if(fabs(*ptr) < Z_prec) continue;
		Zidxs[x] = *ptr;
		maxIdx = x;
	}

	gsl_matrix_free(M);
	gsl_vector_free(ans);
	gsl_vector_free(tau);
	gsl_vector_free(residual);

	if(lastIdx) *lastIdx = maxIdx;
	return Zidxs;
}

Считает замечательно: для опорного набора коэффициентов (по которому генерировался «волновой фронт»)
double IdxsOri[] = {2., // смещение
	1.1, -0.8, // наклон
	5.5, -3.2, 0., // астигматизм, дефокус, аст.
	6.8, 5.5, 0., 0.,// трилистник, кома
	0., 0., 3.3, 1.4, 8.};
после восстановления получилось вот что:
2.000, 1.100, -0.800, 5.500, -3.200, 0.000, 6.800, 5.500, 0.000, 0.000, 0.000, 0.000, 3.300, 1.400, 8.000,
Я, честно говоря, даже не ожидал такой точности на всего-то 256 точках. И, кстати, считает быстро. Тайминги мне добавлять лень, но разложение по первым 6 степеням полиномов Цернике «волнового фронта» на 256 точек выполняется чуть ли не мгновенно.
Однако, рано я радовался: если максимальной степенью сделать 20, результаты получаются отвратительными. Вот где сказывается неортогональность, приводящая к неоднозначности решения.

В общем, реализовать полиномы, ортогональные на диске, мне все-таки придется! Либо же придется модифицировать задачу, постепенно подбирая коэффициенты: скажем, «скармливая» решателю по 3-4 очередных степени полинома (а в качестве значений ВФ брать невязки от предыдущего решения).
This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org

April 2025

S M T W T F S
  1 23 45
67 89101112
13141516171819
20212223242526
27282930   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated May. 22nd, 2025 09:48 am
Powered by Dreamwidth Studios