blob: 063631466795f3d1570698adf8235590ed5fda3e [file] [log] [blame]
/* ----------------------------------------------------------------------- *//**
*
* @file conjugate_gradient.sql_in
*
* @brief SQL function computing Conjugate Gradient
* @date March 2011
*
*
*//* ----------------------------------------------------------------------- */
/**
@addtogroup grp_cg
<div class="toc"><b>Contents</b>
<ul>
<li><a href="#syntax">Function Syntax</a></li>
<li><a href="#examples">Examples</a></li>
<li><a href="#related">Related Topics</a></li>
</ul>
</div>
@brief Finds the solution to the function \f$ \boldsymbol Ax = \boldsymbol b \f$, where \f$A\f$
is a symmetric, positive-definite matrix and \f$x\f$ and \f$ \boldsymbol b \f$ are vectors.
\warning <em> This MADlib method is still in early stage development.
Interface and implementation are subject to change. </em>
This function uses the iterative conjugate gradient method [1] to find a solution to the function: \f[ \boldsymbol Ax = \boldsymbol b \f]
where \f$ \boldsymbol A \f$ is a symmetric, positive definite matrix and \f$x\f$ and \f$ \boldsymbol b \f$ are vectors.
@anchor syntax
@par Function Syntax
Conjugate gradient returns x as an array. It has the following syntax.
<pre class="syntax">
conjugate_gradient( table_name,
name_of_row_values_col,
name_of_row_number_col,
aray_of_b_values,
desired_precision
)
</pre>
Matrix \f$ \boldsymbol A \f$ is assumed to be stored in a table where each row consists of at least two columns: array containing values of a given row, row number:
<pre>{TABLE|VIEW} <em>matrix_A</em> (
<em>row_number</em> FLOAT,
<em>row_values</em> FLOAT[],
)</pre>
The number of elements in each row should be the same.
\f$ \boldsymbol b \f$ is passed as a FLOAT[] to the function.
@anchor examples
@examp
-# Construct matrix A according to structure.
<pre class="example">
SELECT * FROM data;
</pre>
Result:
<pre class="result">
row_num | row_val
&nbsp;--------+---------
1 | {2,1}
2 | {1,4}
(2 rows)
</pre>
-# Call the conjugate gradient function.
<pre class="example">
SELECT conjugate_gradient( 'data',
'row_val',
'row_num',
'{2,1}',
1E-6,1
);
</pre>
<pre class="result">
INFO: COMPUTE RESIDUAL ERROR 14.5655661859659
INFO: ERROR 0.144934004246004
INFO: ERROR 3.12963615962926e-31
INFO: TEST FINAL ERROR 2.90029642185163e-29
conjugate_gradient
&nbsp;--------------------------
{1,-1.31838984174237e-15}
(1 row)
</pre>
@anchor literature
@literature
[1] "Conjugate gradient method" Wikipedia - http://en.wikipedia.org/wiki/Conjugate_gradient_method
@anchor related
@par Related Topics
File conjugate_gradient.sql_in documenting the SQL function.
*/
/**
* @brief Compute conjugate gradient
*
* @param matrix Name of the table containing argument matrix A
* @param val_id Name of the column contains row values
* @param row_id Name of the column contains row number
* @param b Array containing values of b
* @param precision_limit Precision threshold after which process will terminate
* @param verbosity Verbose flag (0 = false, 1 = true)
* @returns Array containing values of x
*
*/
CREATE OR REPLACE FUNCTION MADLIB_SCHEMA.conjugate_gradient(Matrix TEXT, val_id TEXT, row_id TEXT, b FLOAT[], precision_limit FLOAT, verbosity INT) RETURNS FLOAT[] AS $$
declare
r FLOAT[];
p FLOAT[];
x FLOAT[];
k INT;
iter INT = 0;
recidual_refresh INT := 30;
alpha FLOAT;
r_size FLOAT;
r_new_size FLOAT;
Ap FLOAT[];
Ax FLOAT[];
pAp_size FLOAT;
beta FLOAT;
exit_if_no_progess_in INT := 15;
begin
DROP TABLE IF EXISTS X_val;
CREATE TEMP TABLE X_val(
value FLOAT[]
);
DROP TABLE IF EXISTS P_val;
CREATE TEMP TABLE P_val(
value FLOAT[]
);
SELECT INTO k array_upper(b,1);
--INSERT INTO X_val SELECT ARRAY(SELECT random() FROM generate_series(1, k));
EXECUTE 'INSERT INTO X_val SELECT ARRAY(SELECT random() FROM generate_series(1,' ||k|| '))';
LOOP
IF(iter%recidual_refresh = 0)THEN
EXECUTE 'SELECT array_agg(array_dot) FROM (SELECT MADLIB_SCHEMA.array_dot('||val_id||', j.x) FROM (SELECT value AS x FROM X_val) AS j, '|| Matrix ||' ORDER BY '||row_id||' LIMIT '|| k ||') subq' INTO Ax;
SELECT INTO r MADLIB_SCHEMA.array_sub(b, Ax);
SELECT INTO r_size MADLIB_SCHEMA.array_dot(r, r);
IF(verbosity > 0) THEN
RAISE INFO 'COMPUTE RESIDUAL ERROR %', r_size;
END IF;
SELECT INTO p r;
END IF;
iter = iter + 1;
TRUNCATE TABLE P_val;
--INSERT INTO P_val VALUES(p);
EXECUTE 'INSERT INTO P_val(value) VALUES(array['|| array_to_string(p,',') ||'])';
EXECUTE 'SELECT array_agg(array_dot) FROM (SELECT MADLIB_SCHEMA.array_dot('||val_id||', j.p) FROM (SELECT value AS p FROM P_val) AS j,'|| Matrix ||' ORDER BY '||row_id||' LIMIT '|| k ||') subq' INTO Ap;
SELECT INTO pAp_size MADLIB_SCHEMA.array_dot(p, Ap);
alpha = r_size/pAp_size;
--SELECT INTO x MADLIB_SCHEMA.array_add(value, MADLIB_SCHEMA.array_scalar_mult(p,alpha)) FROM X_val;
EXECUTE 'SELECT MADLIB_SCHEMA.array_add(value, MADLIB_SCHEMA.array_scalar_mult(array['|| array_to_string(p,',') ||']::float[],'||alpha||'::float)) FROM X_val' INTO x;
TRUNCATE TABLE X_val;
--INSERT INTO X_val VALUES(x);
EXECUTE 'INSERT INTO X_val VALUES(array['|| array_to_string(x,',') ||'])';
SELECT INTO r MADLIB_SCHEMA.array_add(r,MADLIB_SCHEMA.array_scalar_mult(Ap, -alpha));
SELECT INTO r_new_size MADLIB_SCHEMA.array_dot(r,r);
IF(verbosity > 0) THEN
RAISE INFO 'ERROR %',r_new_size;
END IF;
IF (r_new_size < precision_limit) THEN
EXECUTE 'SELECT array_agg(array_dot) FROM (SELECT MADLIB_SCHEMA.array_dot('||val_id||', j.x) FROM (SELECT value AS x FROM X_val) AS j, '|| Matrix ||' ORDER BY '||row_id||' LIMIT '|| k ||') subq' INTO Ax;
SELECT INTO r MADLIB_SCHEMA.array_sub(b, Ax);
SELECT INTO r_new_size MADLIB_SCHEMA.array_dot(r, r);
IF(verbosity > 0) THEN
RAISE INFO 'TEST FINAL ERROR %', r_new_size;
END IF;
IF (r_new_size < precision_limit) THEN
EXIT;
END IF;
END IF;
SELECT INTO p MADLIB_SCHEMA.array_add(r, MADLIB_SCHEMA.array_scalar_mult(p, r_new_size/r_size));
IF(r_size < r_new_size) THEN
exit_if_no_progess_in = exit_if_no_progess_in-1;
RAISE INFO 'No progress! count = %',exit_if_no_progess_in;
IF(exit_if_no_progess_in <= 0) THEN
RAISE EXCEPTION 'Algorithm failed to converge. Check is input is positive definate.';
END IF;
ELSE
exit_if_no_progess_in = 15;
END IF;
r_size = r_new_size;
END LOOP;
IF(verbosity > 1) THEN
RETURN ARRAY[r_new_size];
END IF;
--SELECT INTO x value FROM X_val;
EXECUTE 'SELECT value FROM X_val' INTO x;
RETURN x;
end
$$ LANGUAGE plpgsql
m4_ifdef(`__HAS_FUNCTION_PROPERTIES__', `MODIFIES SQL DATA', `');
CREATE OR REPLACE FUNCTION MADLIB_SCHEMA.conjugate_gradient(Matrix TEXT, val_id TEXT, row_id TEXT, b FLOAT[], precision_limit FLOAT) RETURNS FLOAT[] AS $$
declare
begin
RETURN MADLIB_SCHEMA.conjugate_gradient(Matrix, val_id, row_id, b, precision_limit,0);
end
$$ LANGUAGE plpgsql
m4_ifdef(`__HAS_FUNCTION_PROPERTIES__', `MODIFIES SQL DATA', `');