Fungerar va_arg med float* ?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Det börjar ta sig.
Jag vet att det är VLA som jag använder nu.

row_a = 48. column_a = 10.

Detta är den enda funktionen som jag har kvar nu. Sedan är problemet löst.

Kod: Markera allt

static bool opti(float Q[], float c[], float A[], float b[], float x[], size_t row_a, size_t column_a){		
	/* Declare */
	size_t i, j, k;
	
	/* Use Cholesky factorization to solve x from Qx = c because Q is square and symmetric */
	linsolve_chol(Q, x, c, column_a);

	/* Save address */
	float* Ai = A;

	/* Turn x negative */
	for (i = 0; i < column_a; i++) {
		x[i] = -x[i];

		/* If x was nan - return false - Use higher value on lambda constant! */
#ifdef _MSC_VER
		if (_isnanf(x[i])) {
			return false;
		}
#else
		if (isnanf(x[i])) {
			return false;
		}
#endif
	}

	/* Count how many constraints A*x > b */
	//float *K = (float*)malloc(row_a * sizeof(float));
	float K[row_a];
	size_t violations = 0;
	float value;
	for(i = 0; i < row_a; i++){

		/* Check how many rows are A*x > b */
		value = 0.0f;
		for(j = 0; j < column_a; j++){
			value += Ai[j] * x[j];
			/* value += A[i*column_a + j]*x[j]; */
		}
		Ai += column_a;

		/* Constraints difference */
		K[i] = b[i] - value;

		/* Check constraint violation */
		if (K[i] < 0.0f) {
			violations++;
		}
	}

	/* No violation */
	if (violations == 0) {
		//free(K);
		return true;
	}

	/* Solve QP = A' (Notice that we are using a little trick here so we can avoid A') */
	//float *P = (float*)malloc(row_a * column_a * sizeof(float));
	float p[row_a * column_a];
	float* P = p;
	float *P0 = P;
	Ai = A;
	for (i = 0; i < row_a; i++) {
		linsolve_chol(Q, P, Ai, column_a);
		/* linsolve_gauss(Q, P, Ai, column_a, column_a, 0.0f); - Old */
		P += column_a;
		Ai += column_a;
	}
	P = P0;
	tran(P, row_a, column_a);

	/* Multiply H = A*Q*A' */
	//float *H = (float*)malloc(row_a * row_a * sizeof(float));
	float h[row_a * row_a];
	float* H = h;
	float *Hj;
	mul(A, P, H, row_a, column_a, row_a);

	/* Solve lambda from H*lambda = -K, where lambda >= 0 */
	float *lambda = (float*)malloc(row_a * sizeof(float));
	memset(lambda, 0, row_a * sizeof(float));
	float *lambda_p = (float*)malloc(row_a * sizeof(float));
	float w;
	for(i = 0; i < MAX_ITERATIONS; i++){
		/* Update */
		memcpy(lambda_p, lambda, row_a * sizeof(float));

		/* Use Gauss Seidel */
		Hj = H;
		for (j = 0; j < row_a; j++) {
			/* w = H(i, :)*lambda */
			w = 0.0f;
			for (k = 0; k < row_a; k++) {
				w += Hj[k] * lambda[k];
			}
			
			/* Find a solution */
			w = -1.0f / Hj[j] * (K[j] + w - Hj[j]*lambda[j]);
			Hj += row_a;
			lambda[j] = vmax(0.0f, w);
		}

		/* Check if we should break - Found the optimal solution */
		w = 0.0f;
		for (j = 0; j < row_a; j++) {
			value = lambda[j] - lambda_p[j];
			w += value * value;
		}
#ifdef _MSC_VER
		if (w < MIN_VALUE || _isnanf(w)) {
			break;
		}
#else
		if (w < MIN_VALUE || isnanf(w)) {
			break;
		}
#endif
	}

	/* Solve x = x + P*lambda (Notice that x is negative (see above)) */
	float *Plambda = (float*)malloc(column_a * sizeof(float));
	mul(P, lambda, Plambda, column_a, row_a, 1);
	for (j = 0; j < column_a; j++) {
		x[j] -= Plambda[j];
	}

	/* Free */
	//free(K);
	//free(P);
	//free(H);
	free(lambda);
	free(lambda_p);
	free(Plambda);

	/* If w was nan - return false */
#ifdef _MSC_VER
	if (_isnanf(w)) {
		return false;
	}
#else
	if (isnanf(w)) {
		return false;
	}
#endif

	/* If i equal to MAX_ITERATIONS, then it did not find a solution */
	return i < MAX_ITERATIONS;
}
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Är det någon som har varit med om att en const size_t har ändrat sig efter en beräkning?
När jag kör denna kod.

Kod: Markera allt

#include <string.h>
#include <float.h>
#include <math.h>
#include <stdbool.h>
#define MIN_VALUE 1e-11f

bool chol(const float A[], float L[], const size_t row) {
	/* Save address */
	float* Li = L;
	float* Lj;
	const float* Ai = A;

	memset(L, 0, row * row * sizeof(float));
	float s;
	size_t i, j, k;
	for (i = 0; i < row; i++) {
		Lj = L;
		for (j = 0; j <= i; j++) {
			s = 0.0f;
			for (k = 0; k < j; k++) {
				s += Li[k] * Lj[k]; /* s += L[row * i + k] * L[row * j + k]; */
			}

			/* We cannot divide with zero L[row * j + j] */
			if (fabsf(Lj[j]) < MIN_VALUE) {
				Lj[j] = FLT_EPSILON;
				/* L[row * j + j] = FLT_EPSILON; // Same as eps command in MATLAB */
			}
			Li[j] = (i == j) ? sqrtf(Ai[i] - s) : (1.0f / Lj[j] * (Ai[j] - s));
			/* L[row * i + j] = (i == j) ? sqrtf(A[row * i + i] - s) : (1.0f / L[row * j + j] * (A[row * i + j] - s)); */
			Lj += row;
		}
		Li += row;
		Ai += row;
	}
	
	return true;
}
Direkt efter memset(L, 0, row * row * sizeof(float)); så ändras row == 10 till row == 0.

Vad kallar vi detta? Minnesläcka? Arrayen L är dock inte NULL :humm:
Användarvisningsbild
mankan
EF Sponsor
Inlägg: 934
Blev medlem: 18 juli 2015, 11:23:22
Ort: Linköping

Re: Fungerar va_arg med float* ?

Inlägg av mankan »

Hur är L allokerad? Innehåller den row*row floats?

Jag gissar du skriver sönder stacken för dig själv.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Så här är den allokerad.
row är const size_t row = 10

Kod: Markera allt

	float* L = (float*)malloc(row * row * sizeof(float));
	/* Test */
	if(L == NULL){
		x[0] = 1;
	}
	const bool status = chol(A, L, row);
Edit:
Allt verkar fungera bra när jag kör statisk allokering, dvs float L[10*10].
Så den där NULL-kollen verkar lite lurig.
Är det någon som kan ge en fingervisning på vad man ska ändra på för att den ska kunna klara utav att allokera 10*10*4 = 400 bytes på heap?

Eventuellt måste jag hitta en snålare algoritm som kan lösa cholesky faktorisering för linjära system A*x = b där A är symmetrisk och positiv definitiv.
hawkan
Inlägg: 3382
Blev medlem: 14 augusti 2011, 10:27:40

Re: Fungerar va_arg med float* ?

Inlägg av hawkan »

Vad du ska ändra på är väl att ta en processor med mycket mera minne. Ram.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Jag vet, men jag är så nära nu målet. :) Håller på skriva om en kvadratisk programmeringsalgoritm så den inte använder/använder lite dynamisk allokering.
Det är där problemet sitter i.

Vi kan börja med Matlab koden.

Kod: Markera allt

    /* GNU Octave code:
     *
	% This is quadratic programming with Hildreth's method
	% Min 1/2x^TQx + c^Tx
	% S.t Ax <= b
	%
	% If you want to do maximization, then turn Q and c negative. The constraints are the same
	%
	% Max 1/2x^T(-Q)x + (-c)^Tx
	% S.t Ax <= b
	%
	% Input: Q(Symmetric matrix), c(Objective function), A(Constraint matrix), b(Constraint vector)
	% Output: x(Solution vector), solution(boolean flag)
	% Example 1: [x, solution] = quadprog(Q, c, A, b)

	function [x, solution] = quadprog(Q, c, A, b)
	  % Assume that the solution is true
	  solution = true;

	  % Same as in C code for Functions.h at CControl
	  MIN_VALUE = 1e-11;
	  MAX_ITERATIONS = 10000;

	  % Unconstrained solution
	  x = -linsolve(Q, c);

	  % Constraints difference
	  K = b - A*x;

	  % Check constraint violation
	  if(sum(K < 0) == 0)
		return; % No violation
	  end

	  % Create P
	  P = linsolve(Q, A');

	  % Create H = A*Q*A'
	  H = A*P;

	  % Solve lambda from H*lambda = -K, where lambda >= 0
	  [m, n] = size(K);
	  lambda = zeros(m, n);
	  for km = 1:MAX_ITERATIONS
		lambda_p = lambda;

		% Use Gauss Seidel
		for i = 1:m
		  w = -1.0/H(i,i)*(K(i) + H(i,:)*lambda - H(i,i)*lambda(i));
		  lambda(i) = max(0, w);
		end

		% Check if the minimum convergence has been reached
		w = (lambda - lambda_p)'*(lambda - lambda_p);
		if (w < MIN_VALUE)
		  break;
		end

		% Check if the maximum iteration have been reached
		if(km == MAX_ITERATIONS)
		  solution = false;
		  return;
		end
	  end

	  % Find the solution: x = -inv(Q)*c - inv(Q)*A'*lambda
	  x = x - P*lambda;
	end
 */
Denna kod kan enkelt översättas till detta

Kod: Markera allt

static bool opti(const float Q[], const float c[], const float A[], const float b[], float x[], const size_t row_a, const size_t column_a){
	/* Declare */
	size_t i, j, k;
	
	/* Use Cholesky factorization to solve x from Qx = c because Q is square and symmetric */
	linsolve_chol(Q, x, c, column_a);

	/* Save address */
	const float* Ai = A;

	/* Turn x negative */
	for (i = 0; i < column_a; i++) {
		x[i] = -x[i];

		/* If x was nan - return false - Use higher value on lambda constant! */
#ifdef _MSC_VER
		if (_isnanf(x[i])) {
			return false;
		}
#else
		if (isnanf(x[i])) {
			return false;
		}
#endif
	}

	/* Count how many constraints A*x > b */
	float *K = (float*)malloc(row_a * sizeof(float));
	size_t violations = 0;
	float value;
	for(i = 0; i < row_a; i++){

		/* Check how many rows are A*x > b */
		value = 0.0f;
		for(j = 0; j < column_a; j++){
			value += Ai[j] * x[j];
			/* value += A[i*column_a + j]*x[j]; */
		}
		Ai += column_a;

		/* Constraints difference */
		K[i] = b[i] - value;

		/* Check constraint violation */
		if (K[i] < 0.0f) {
			violations++;
		}
	}

	/* No violation */
	if (violations == 0) {
		free(K);
		return true;
	}

	/* Solve QP = A' (Notice that we are using a little trick here so we can avoid A') */
	float *P = (float*)malloc(row_a * column_a * sizeof(float));
	float *P0 = P;
	Ai = A;
	for (i = 0; i < row_a; i++) {
		linsolve_chol(Q, P, Ai, column_a);
		/* linsolve_gauss(Q, P, Ai, column_a, column_a, 0.0f); - Old */
		P += column_a;
		Ai += column_a;
	}
	P = P0;
	tran(P, row_a, column_a);

	/* Multiply H = A*Q*A' */
	float *H = (float*)malloc(row_a * row_a * sizeof(float));
	float *Hj;
	mul(A, P, H, row_a, column_a, row_a);

	/* Solve lambda from H*lambda = -K, where lambda >= 0 */
	float *lambda = (float*)malloc(row_a * sizeof(float));
	memset(lambda, 0, row_a * sizeof(float));
	float *lambda_p = (float*)malloc(row_a * sizeof(float));
	float w;
	for(i = 0; i < MAX_ITERATIONS; i++){
		/* Update */
		memcpy(lambda_p, lambda, row_a * sizeof(float));

		/* Use Gauss Seidel */
		Hj = H;
		for (j = 0; j < row_a; j++) {
			/* w = H(i, :)*lambda */
			w = 0.0f;
			for (k = 0; k < row_a; k++) {
				w += Hj[k] * lambda[k];
			}
			
			/* Find a solution */
			w = -1.0f / Hj[j] * (K[j] + w - Hj[j]*lambda[j]);
			Hj += row_a;
			lambda[j] = vmax(0.0f, w);
		}

		/* Check if we should break - Found the optimal solution */
		w = 0.0f;
		for (j = 0; j < row_a; j++) {
			value = lambda[j] - lambda_p[j];
			w += value * value;
		}
#ifdef _MSC_VER
		if (w < MIN_VALUE || _isnanf(w)) {
			break;
		}
#else
		if (w < MIN_VALUE || isnanf(w)) {
			break;
		}
#endif
	}

	/* Solve x = x + P*lambda (Notice that x is negative (see above)) */
	float *Plambda = (float*)malloc(column_a * sizeof(float));
	mul(P, lambda, Plambda, column_a, row_a, 1);
	for (j = 0; j < column_a; j++) {
		x[j] -= Plambda[j];
	}

	/* Free */
	free(K);
	free(P);
	free(H);
	free(lambda);
	free(lambda_p);
	free(Plambda);

	/* If w was nan - return false */
#ifdef _MSC_VER
	if (_isnanf(w)) {
		return false;
	}
#else
	if (isnanf(w)) {
		return false;
	}
#endif

	/* If i equal to MAX_ITERATIONS, then it did not find a solution */
	return i < MAX_ITERATIONS;
}
Men för att minimera dynamisk allokering så kan man använda flera for-satser. Som ni ser så måste det fungera säkert väldigt bra att bara ta en rad i taget.

Kod: Markera allt

static bool optislim(const float Q[], const float c[], const float A[], const float b[], float x[], const size_t row_a, const size_t column_a){
	/* Declare */
	size_t i, j, k, l;
	
	/* Use Cholesky factorization to solve x from Qx = c because Q is square and symmetric */
	linsolve_chol(Q, x, c, column_a);

	/* Save address */
	const float* Ai = A;

	/* Turn x negative */
	for (i = 0; i < column_a; i++) {
		x[i] = -x[i];

		/* If x was nan - return false - Use higher value on lambda constant! */
#ifdef _MSC_VER
		if (_isnanf(x[i])) {
			return false;
		}
#else
		if (isnanf(x[i])) {
			return false;
		}
#endif
	}

	/* Count how many constraints A*x > b */
	float K, value;
	for (i = 0; i < row_a; i++) {

		/* Check how many rows are A*x > b */
		value = 0.0f;
		for(j = 0; j < column_a; j++){
			value += Ai[j] * x[j];
			/* value += A[i*column_a + j]*x[j]; */
		}

		/* Constraints difference */
		K = b[i] - value;

		/* Check constraint violation */
		if (K < 0.0f) {
			/* Solve QP = A' (Notice that we are using a little trick here so we can avoid A') */
			float* P = (float*)malloc(column_a * sizeof(float));
			linsolve_chol(Q, P, Ai, column_a);

			/* Multiply H = A*Q*A' */
			for (k = 0; k < row_a; k++) {
				value = 0.0f;
				for (l = 0; l < column_a; l++) {
					value += A[k * column_a + l] * P[l];
				}

				for (l = 0; l < MAX_ITERATIONS; l++) {

				}

			}

			/* Free */
			free(P);
		}

		Ai += column_a;
	}

	/* No violation */
	return true;

	
	/* Solve lambda from H*lambda = -K, where lambda >= 0 */
	float *lambda = (float*)malloc(row_a * sizeof(float));
	memset(lambda, 0, row_a * sizeof(float));
	float *lambda_p = (float*)malloc(row_a * sizeof(float));
	float w;
	for(i = 0; i < MAX_ITERATIONS; i++){
		/* Update */
		memcpy(lambda_p, lambda, row_a * sizeof(float));

		/* Use Gauss Seidel */
		Hj = H;
		for (j = 0; j < row_a; j++) {
			/* w = H(i, :)*lambda */
			w = 0.0f;
			for (k = 0; k < row_a; k++) {
				w += Hj[k] * lambda[k];
			}
			
			/* Find a solution */
			w = -1.0f / Hj[j] * (K[j] + w - Hj[j]*lambda[j]);
			Hj += row_a;
			lambda[j] = vmax(0.0f, w);
		}

		/* Check if we should break - Found the optimal solution */
		w = 0.0f;
		for (j = 0; j < row_a; j++) {
			value = lambda[j] - lambda_p[j];
			w += value * value;
		}
#ifdef _MSC_VER
		if (w < MIN_VALUE || _isnanf(w)) {
			break;
		}
#else
		if (w < MIN_VALUE || isnanf(w)) {
			break;
		}
#endif
	}

	/* Solve x = x + P*lambda (Notice that x is negative (see above)) */
	float *Plambda = (float*)malloc(column_a * sizeof(float));
	mul(P, lambda, Plambda, column_a, row_a, 1);
	for (j = 0; j < column_a; j++) {
		x[j] -= Plambda[j];
	}

	/* Free */
	free(K);
	free(P);
	free(H);
	free(lambda);
	free(lambda_p);
	free(Plambda);

	/* If w was nan - return false */
#ifdef _MSC_VER
	if (_isnanf(w)) {
		return false;
	}
#else
	if (isnanf(w)) {
		return false;
	}
#endif

	/* If i equal to MAX_ITERATIONS, then it did not find a solution */
	return i < MAX_ITERATIONS;
}
hawkan
Inlägg: 3382
Blev medlem: 14 augusti 2011, 10:27:40

Re: Fungerar va_arg med float* ?

Inlägg av hawkan »

Nja, alltså finns det tillräckligt med minne kommer malloc att fungera även om man inte borde använda det.
Det avdelas en mängd minne till heap som malloc använder, men både stack och heap tas från ram, stack underifrån, heap från max ner t ex. På något sätt. Ta reda på hur minne avdelas för heap och hur du kan mäta och påverka det. Och se om det alls går att få plats med alla variabler.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Jag tänker samma sak. Om det går att allokera statiskt, så måste det gå att allokera dynamiskt.
hawkan
Inlägg: 3382
Blev medlem: 14 augusti 2011, 10:27:40

Re: Fungerar va_arg med float* ?

Inlägg av hawkan »

I princip ja, men minnet är uppdelat, malloc tar inte från stacken utan från heap. Kan vara att malloc startar från max minne och går nedåt.
Så du får ha koll på hur uppdelningen görs, om du kan ändra den om det behövs så du får mer minne till heap (malloc).
hummel
Inlägg: 2520
Blev medlem: 28 november 2009, 10:40:52
Ort: Stockholm

Re: Fungerar va_arg med float* ?

Inlägg av hummel »

DanielM, du får titta i minneskartan och verifiera exakt vad och var ditt minne är allokerat och används. Hur mycket ledigt har du på stack respektive heap under exekvering?
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Är det någon funktion i IDE:n eller?
Jag använder mig av STM32CubeIDE (Eclipse) och jag har inte sett något där man kan i realtid kolla hur mycket minne förbrukas för varje rad av kod.

Edit:
Jag har kommit vidare nu. Jag återanvände arrayer och minskade storlekar på dom. Nu så kan jag köra chol utan problem. Men ännu är jag inte i mål!

Jag har kommit till "sista bossen" nu kan man väll säga. En array på 48*48 som har 32-bit float. :twisted:
hummel
Inlägg: 2520
Blev medlem: 28 november 2009, 10:40:52
Ort: Stockholm

Re: Fungerar va_arg med float* ?

Inlägg av hummel »

Vissa utvecklingssystem har det andra inte, kanske finns det tillägg till ditt? Kanske debuggern har den funktionen.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

Cortex M0 har dock inte detta tyvärr.
Men som tur så har jag rundat av det svåra nu.

Nu allokerar jag som mest (48*10 + 48 + 100)*4 = 2512 bytes under körningen av quadprog.

Jag tror det inte går att optimera mera. Ursprungligen var det 12152 bytes som quadprog tog i bytes.

Ska presentera koden.

P.S Är det någon här som vill lära sig lite om MPC och motiveringen till användningen av MPC?
Senast redigerad av DanielM 13 februari 2025, 12:21:37, redigerad totalt 1 gång.
hawkan
Inlägg: 3382
Blev medlem: 14 augusti 2011, 10:27:40

Re: Fungerar va_arg med float* ?

Inlägg av hawkan »

Och processorn har hur mycket ram?
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Fungerar va_arg med float* ?

Inlägg av DanielM »

8 kB :vissla:
STM32F051 heter processorn.
Skriv svar