/* --------------------------------------------------------------------------
CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-17 Bradley M. Bell

CppAD is distributed under multiple licenses. This distribution is under
the terms of the
                    GNU General Public License Version 3.

A copy of this license is included in the COPYING file of this distribution.
Please visit http://www.coin-or.org/CppAD/ for information on other licenses.
-------------------------------------------------------------------------- */
/*
$begin cppad_sparse_jacobian.cpp$$
$spell
	const
	ifdef
	ifndef
	colpack
	boolsparsity
	namespace
	onetape
	work work
	jac
	CppAD
	cppad
	hpp
	bool
	typedef
	endif
	tmp
	std
	var
	cout
	endl
	Jacobian
	Fp
$$

$section CppAD Speed: Sparse Jacobian$$
$mindex link_sparse_jacobian speed$$


$head Specifications$$
See $cref link_sparse_jacobian$$.

$head Implementation$$

$srccode%cpp% */
# include <cppad/cppad.hpp>
# include <cppad/speed/uniform_01.hpp>
# include <cppad/speed/sparse_jac_fun.hpp>

// Note that CppAD uses global_option["memory"] at the main program level
# include <map>
extern std::map<std::string, bool> global_option;

namespace {
	using CppAD::vector;
	typedef vector<size_t>  s_vector;
	typedef vector<bool>    b_vector;

	void calc_sparsity(
		CppAD::sparse_rc<s_vector>& sparsity ,
		CppAD::ADFun<double>&       f        )
	{	bool reverse       = global_option["revsparsity"];
		bool transpose     = false;
		bool internal_bool = global_option["boolsparsity"];
		bool dependency    = false;
		bool subgraph      = global_option["subsparsity"];
		size_t n = f.Domain();
		size_t m = f.Range();
		if( subgraph )
		{	b_vector select_domain(n), select_range(m);
			for(size_t j = 0; j < n; ++j)
				select_domain[j] = true;
			for(size_t i = 0; i < m; ++i)
				select_range[i] = true;
			f.subgraph_sparsity(
				select_domain, select_range, transpose, sparsity
			);
		}
		else
		{	size_t q = n;
			if( reverse )
				q = m;
			//
			CppAD::sparse_rc<s_vector> identity;
			identity.resize(q, q, q);
			for(size_t k = 0; k < q; k++)
				identity.set(k, k, k);
			//
			if( reverse )
			{	f.rev_jac_sparsity(
					identity, transpose, dependency, internal_bool, sparsity
				);
			}
			else
			{	f.for_jac_sparsity(
					identity, transpose, dependency, internal_bool, sparsity
				);
			}
		}
	}
}

bool link_sparse_jacobian(
	size_t                           size     ,
	size_t                           repeat   ,
	size_t                           m        ,
	const CppAD::vector<size_t>&     row      ,
	const CppAD::vector<size_t>&     col      ,
	      CppAD::vector<double>&     x        ,
	      CppAD::vector<double>&     jacobian ,
	      size_t&                    n_sweep  )
{
	// --------------------------------------------------------------------
	// check global options
	const char* valid[] = {
		"memory", "onetape", "optimize", "subgraph",
# if CPPAD_HAS_COLPACK
		"boolsparsity", "revsparsity", "subsparsity", "colpack"
# else
		"boolsparsity", "revsparsity", "subsparsity"
# endif
	};
	size_t n_valid = sizeof(valid) / sizeof(valid[0]);
	typedef std::map<std::string, bool>::iterator iterator;
	//
	for(iterator itr=global_option.begin(); itr!=global_option.end(); ++itr)
	{	if( itr->second )
		{	bool ok = false;
			for(size_t i = 0; i < n_valid; i++)
				ok |= itr->first == valid[i];
			if( ! ok )
				return false;
		}
	}
	if( global_option["subsparsity"] )
	{	if( global_option["boolsparisty"] || global_option["revsparsity"] )
			return false;
	}
	// ---------------------------------------------------------------------
	// optimization options: no conditional skips or compare operators
	std::string options="no_compare_op";
	// -----------------------------------------------------
	// setup
	typedef CppAD::AD<double>    a_double;
	typedef vector<double>       d_vector;
	typedef vector<a_double>     ad_vector;
	//
	size_t order = 0;         // derivative order corresponding to function
	size_t n     = size;      // number of independent variables
	ad_vector  a_x(n);        // AD domain space vector
	ad_vector  a_y(m);        // AD range space vector y = f(x)
	CppAD::ADFun<double> f;   // AD function object
	//
	// declare sparsity pattern
	CppAD::sparse_rc<s_vector>  sparsity;
	//
	// declare subset where Jacobian is evaluated
	CppAD::sparse_rc<s_vector> subset_pattern;
	size_t nr  = m;
	size_t nc  = n;
	size_t nnz = row.size();
	subset_pattern.resize(nr, nc, nnz);
	for(size_t k = 0; k < nnz; k++)
		subset_pattern.set(k, row[k], col[k]);
	CppAD::sparse_rcv<s_vector, d_vector> subset( subset_pattern );
	const d_vector& subset_val( subset.val() );
	//
	// coloring method
	std::string coloring = "cppad";
# if CPPAD_HAS_COLPACK
	if( global_option["colpack"] )
		coloring = "colpack";
# endif
	//
	// maximum number of colors at once
	size_t group_max = 25;
	// ------------------------------------------------------
	if( ! global_option["onetape"] ) while(repeat--)
	{	// choose a value for x
		CppAD::uniform_01(n, x);
		for(size_t j = 0; j < n; j++)
			a_x[j] = x[j];
		//
		// declare independent variables
		Independent(a_x);
		//
		// AD computation of f(x)
		CppAD::sparse_jac_fun<a_double>(m, n, a_x, row, col, order, a_y);
		//
		// create function object f : X -> Y
		f.Dependent(a_x, a_y);
		//
		if( global_option["optimize"] )
			f.optimize(options);
		//
		// skip comparison operators
		f.compare_change_count(0);
		//
		// calculate the Jacobian sparsity pattern for this function
		calc_sparsity(sparsity, f);
		//
		if( global_option["subgraph"] )
		{	// user reverse mode becasue forward not yet implemented
			f.subgraph_jac_rev(x, subset);
			n_sweep = 0;
		}
		else
		{	// structure that holds some of the work done by sparse_jac_for
			CppAD::sparse_jac_work work;
			//
			// calculate the Jacobian at this x
			// (use forward mode because m > n ?)
			n_sweep = f.sparse_jac_for(
				group_max, x, subset, sparsity, coloring, work
			);
		}
		for(size_t k = 0; k < nnz; k++)
			jacobian[k] = subset_val[k];
	}
	else
	{	// choose a value for x
		CppAD::uniform_01(n, x);
		for(size_t j = 0; j < n; j++)
			a_x[j] = x[j];
		//
		// declare independent variables
		Independent(a_x);
		//
		// AD computation of f(x)
		CppAD::sparse_jac_fun<a_double>(m, n, a_x, row, col, order, a_y);
		//
		// create function object f : X -> Y
		f.Dependent(a_x, a_y);
		//
		if( global_option["optimize"] )
			f.optimize(options);
		//
		// skip comparison operators
		f.compare_change_count(0);
		//
		// calculate the Jacobian sparsity pattern for this function
		calc_sparsity(sparsity, f);
		//
		// structure that holds some of the work done by sparse_jac_for
		CppAD::sparse_jac_work work;
		//
		while(repeat--)
		{	// choose a value for x
			CppAD::uniform_01(n, x);
			//
			// calculate the Jacobian at this x
			if( global_option["subgraph"] )
			{	// user reverse mode becasue forward not yet implemented
				f.subgraph_jac_rev(x, subset);
				n_sweep = 0;
			}
			else
			{	// (use forward mode because m > n ?)
				n_sweep = f.sparse_jac_for(
					group_max, x, subset, sparsity, coloring, work
				);
			}
			for(size_t k = 0; k < nnz; k++)
				jacobian[k] = subset_val[k];
		}
	}
	return true;
}
/* %$$
$end
*/
