13#if HAVE_SUITESPARSE_CHOLMOD_H
14#include <suitesparse/cholmod.h>
19#if HAVE_OPENBLAS_CBLAS_H
20# include <openblas/cblas.h>
21#elif HAVE_CBLAS_OPENBLAS_H
22# include <cblas_openblas.h>
23#elif HAVE_CBLAS_HYPHEN_OPENBLAS_H
24# include <cblas-openblas.h>
25#elif HAVE_ACCELERATE_ACCELERATE_H
26# include <Accelerate/Accelerate.h>
31# define I_LOVE_FORTRAN 1
32#elif HAVE_OPENBLAS_F77BLAS_H
33# include <openblas/f77blas.h>
34# define I_LOVE_FORTRAN 1
45#include "union_find.h"
69typedef enum VertexType_ {VERTEX_SPOB, VERTEX_JUMP}
VertexType;
72typedef struct Vertex_ {
82typedef struct Faction_ {
84 double lane_length_per_presence;
85 double lane_base_cost;
95static cholmod_common
C;
111static cholmod_sparse *
QtQ;
114static cholmod_dense **
PPl;
145static int cmp_key(
const void* p1,
const void* p2 );
146static inline void triplet_entry( cholmod_triplet* m,
int i,
int j,
double v );
148static cholmod_dense*
ncholmod_ddmult( cholmod_dense* A,
int transA, cholmod_dense* B );
180 cholmod_finish( &
C );
215 if ((standing & SAFELANES_FRIENDLY) && fa)
217 if ((standing & SAFELANES_NEUTRAL) && !fe)
219 if ((standing & SAFELANES_HOSTILE) && fe)
226 for (
int j=0; j<2; j++)
231 for (
int j=0; j<2; j++) {
232 switch (v[j]->type) {
235 l->
point_id[j] = system->spobs[v[j]->index]->id;
239 l->
point_id[j] = system->jumps[v[j]->index].targetid;
242 ERR( _(
"Safe-lane vertex type is invalid.") );
254 Uint32 time = SDL_GetTicks();
266 time = SDL_GetTicks() - time;
299 cholmod_free_dense( &
PPl[i], &
C );
302 cholmod_free_dense( &
utilde, &
C );
303 cholmod_free_dense( &
ftilde, &
C );
304 cholmod_free_sparse( &
QtQ, &
C );
305 cholmod_free_triplet( &
stiff, &
C );
313 cholmod_sparse *stiff_s;
314 cholmod_factor *stiff_f;
315 cholmod_dense *_QtQutilde, *Lambda_tilde, *Y_workspace, *E_workspace;
317 double zero[] = {0, 0}, neg_1[] = {-1, 0};
319 Y_workspace = E_workspace = Lambda_tilde = NULL;
320 stiff_s = cholmod_triplet_to_sparse(
stiff, 0, &
C );
321 stiff_f = cholmod_analyze( stiff_s, &
C );
322 cholmod_factorize( stiff_s, stiff_f, &
C );
323 cholmod_solve2( CHOLMOD_A, stiff_f,
ftilde, NULL, &
utilde, NULL, &Y_workspace, &E_workspace, &
C );
324 _QtQutilde = cholmod_zeros(
utilde->nrow,
utilde->ncol, CHOLMOD_REAL, &
C );
325 cholmod_sdmult(
QtQ, 0, neg_1, zero,
utilde, _QtQutilde, &
C );
326 cholmod_solve2( CHOLMOD_A, stiff_f, _QtQutilde, NULL, &Lambda_tilde, NULL, &Y_workspace, &E_workspace, &
C );
327 cholmod_free_dense( &_QtQutilde, &
C );
328 cholmod_free_dense( &Y_workspace, &
C );
329 cholmod_free_dense( &E_workspace, &
C );
330 cholmod_free_factor( &stiff_f, &
C );
331 cholmod_free_sparse( &stiff_s, &
C );
333 cholmod_free_dense( &Lambda_tilde, &
C );
335 return turns_next_time;
364 if (sys_isFlag( sys, SYSTEM_NOLANES )) {
369 for (
int i=0; i<
array_size(sys->spobs); i++) {
370 const Spob *p = sys->spobs[i];
371 if (spob_isFlag( p, SPOB_NOLANES ))
373 if (p->presence.base!=0. || p->presence.bonus!=0.) {
374 Vertex v = {.system = system, .type = VERTEX_SPOB, .index = i};
380 for (
int i=0; i<
array_size(sys->jumps); i++) {
381 const JumpPoint *jp = &sys->jumps[i];
382 if (jp_isFlag( jp, JP_HIDDEN | JP_EXITONLY | JP_NOLANES ))
384 Vertex v = {.system = system, .type = VERTEX_JUMP, .index = i};
386 if (jp->targetid < system && jp->returnJump != NULL)
419 double lij = vec2_dist( pi, pj );
420 int has_approx_midpoint = 0;
423 has_approx_midpoint = 1;
426 if (has_approx_midpoint)
453 for (
int fi=0; fi<
array_size(faction_all); fi++) {
454 int f = faction_all[fi];
491 for (
int i=0; i<
array_size(anchor_systems); i++)
547 double max_conductivity;
549 cholmod_free_triplet( &
stiff, &
C );
573 assert( cholmod_check_triplet(
stiff, &
C) );
584 double *sv =
stiff->x;
594 double *sv =
stiff->x;
595 for (
int i=3*ei_activated; i<3*(ei_activated+1); i++)
606 cholmod_free_sparse( &
QtQ, &
C );
612 ((
int*)Q->p)[i+1] = 2*(i+1);
615 ((
double*)Q->x)[2*i+0] = +1;
616 ((
double*)Q->x)[2*i+1] = -1;
619 assert( cholmod_check_sparse( Q, &
C ) );
622 cholmod_free_sparse( &Q, &
C );
632 cholmod_free_dense( &
ftilde, &
C );
633 ftilde = cholmod_sparse_to_dense( sp, &
C );
634 cholmod_free_sparse( &sp, &
C );
635 cholmod_free_sparse( &eye, &
C );
647 cholmod_free_dense( &
PPl[fi], &
C );
656 component = calloc( np,
sizeof(
int) );
657 for (
int i=0; i<np; i++)
660 for (
int i=0; i<np; i++) {
669 for (
int j=0; j<i; j++)
670 if (component[i] == component[j])
672 for (
int j=i+1; j<np; j++)
673 if (component[i] == component[j])
679 for (
int i=0; i<np; i++)
680 for (
int j=0; j<i; j++) {
681 double d = ((
double*)
PPl[fi]->x)[np*i+j];
683 ((
double*)
PPl[fi]->x)[np*i+j] = -
d;
684 ((
double*)
PPl[fi]->x)[np*j+i] = -
d;
685 ((
double*)
PPl[fi]->x)[np*i+i] +=
d;
686 ((
double*)
PPl[fi]->x)[np*j+j] +=
d;
698 int *facind_opts, *edgeind_opts, turns_next_time;
699 double *facind_vals, Linv;
701 size_t *lal_bases, lal_base;
724 double cost_best, cost_cheapest_other;
725 int fi = facind_opts[fii];
727 double score_best = 0.;
734 lal_base = lal_bases[fi];
760 ei_best = edgeind_opts[0];
762 cost_cheapest_other = +HUGE_VAL;
765 for (
int eii=0; eii<
array_size(edgeind_opts); eii++) {
766 int ei = edgeind_opts[eii];
772 if (lal[fi] == NULL) {
775 cholmod_free_dense( &lamt, &
C );
785 score *=
ALPHA * Linv * Linv;
789 if (score < score_best) {
792 cost_cheapest_other =
MIN( cost_cheapest_other, cost_best );
796 cost_cheapest_other =
MIN( cost_cheapest_other, cost );
801 if (score_best >= 0.)
823 assert(
"Correctly tracked row offsets between the 'lal' and 'utilde' matrices" && lal[fi]->nrow == lal_bases[fi] );
827 cholmod_free_dense( &lal[fi], &
C );
834 return turns_next_time;
838static int cmp_key(
const void* p1,
const void* p2 )
849 const double MAX_COSINE = cos(
MIN_ANGLE);
850 double lnp = vec2_dist( n, p );
851 double lmp = vec2_dist( m, p );
852 double dpn = ((n->x-m->
x)*(n->x-p->x) + (n->y-m->
y)*(n->y-p->y)) / ( lmn * lnp );
853 double dpm = ((m->
x-n->x)*(m->
x-p->x) + (m->
y-n->y)*(m->
y-p->y)) / ( lmn * lmp );
854 return (dpn > MAX_COSINE && lnp < lmn) || (dpm > MAX_COSINE && lmp < lmn);
869 ERR( _(
"Safe-lane vertex type is invalid.") );
885 ERR( _(
"Safe-lane vertex type is invalid.") );
915 return (m1 & m2) ? (m1 & m2) : (m1 | m2);
918static inline void triplet_entry( cholmod_triplet* m,
int i,
int j,
double x )
920 ((
int*)m->i)[m->nnz] = i;
921 ((
int*)m->j)[m->nnz] = j;
922 ((
double*)m->x)[m->nnz] = x;
931 size_t nr, nc, in_r, out_r;
937 if (sysPresence[si] > 0)
940 out = cholmod_allocate_dense( nr, nc, nr, CHOLMOD_REAL, &
C );
945 if (sysPresence[si] > 0) {
946 for (
size_t c = 0;
c < nc;
c++)
947 memcpy( &((
double*)out->x)[
c*out->d + out_r], &((
double*)m->x)[
c*m->d + in_r], sz *
sizeof(
double) );
956static cholmod_dense*
ncholmod_ddmult( cholmod_dense* A,
int transA, cholmod_dense* B )
959 blasint M = transA ? A->ncol : A->nrow, K = transA ? A->nrow : A->ncol, N = B->ncol, lda = A->d, ldb = B->d, ldc = M;
960 assert( K == (blasint) B->nrow );
961 cholmod_dense *out = cholmod_allocate_dense( M, N, M, CHOLMOD_REAL, &
C );
962 double alpha = 1., beta = 0.;
963 BLASFUNC(dgemm)( transA ?
"T" :
"N",
"N", &M, &N, &K, &alpha, A->x, &lda, B->x, &ldb, &beta, out->x, &ldc);
965 size_t M = transA ? A->ncol : A->nrow, K = transA ? A->nrow : A->ncol, N = B->ncol;
966 assert( K == B->nrow );
967 cholmod_dense *out = cholmod_allocate_dense( M, N, M, CHOLMOD_REAL, &
C );
968 cblas_dgemm( CblasColMajor, transA?CblasTrans:CblasNoTrans, CblasNoTrans, M, N, K, 1, A->x, A->d, B->x, B->d, 0, out->x, out->d);
976 assert( A->ncol == B->ncol );
978 blasint N = A->ncol, incA = A->d, incB = B->d;
979 return BLASFUNC(ddot)( &N, (
double*)A->x + i, &incA, (
double*)B->x + j, &incB);
981 return cblas_ddot( A->ncol, (
double*)A->x + i, A->d, (
double*)B->x + j, B->d);
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
#define array_resize(ptr_array, new_size)
Resizes the array to accomodate new_size elements.
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
#define array_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
StarSystem * systems_stack
int areEnemies(int a, int b)
Checks whether two factions are enemies.
double faction_lane_base_cost(int f)
Gets the faction's weight for patrolled safe-lane construction;.
double faction_lane_length_per_presence(int f)
Gets the faction's weight for patrolled safe-lane construction (0 means they don't build lanes).
int * faction_getAllVisible(void)
Returns all non-invisible faction IDs in an array (array.h).
int areAllies(int a, int b)
Checks whether two factions are allies or not.
int naev_isQuit(void)
Get if Naev is trying to quit.
Header file with generic functions and naev-specifics.
static int safelanes_calculated_once
static FactionMask MASK_COMPROMISE(int id1, int id2)
A mask with appropriate lane-building rights given one faction ID owning each endpoint.
@ STORAGE_MODE_UPPER_TRIANGULAR_PART
@ STORAGE_MODE_LOWER_TRIANGULAR_PART
@ STORAGE_MODE_UNSYMMETRIC
static FactionMask * vertex_fmask
static cholmod_dense * safelanes_sliceByPresence(cholmod_dense *m, double *sysPresence)
Construct the matrix-slice of m, selecting those rows where the corresponding presence value is posit...
static void array_push_back_edge(Edge **a, int v0, int v1)
Like array_push_back( a, Edge{v0, v1} ), but achievable in C. :-P.
static FactionMask * lane_fmask
static const double MIN_ANGLE
static void safelanes_initStiff(void)
Sets up the stiffness matrix.
static void safelanes_destroyTmp(void)
Tears down the local faction/object stacks.
static const double ALPHA
static cholmod_dense * ftilde
static void safelanes_updateConductivity(int ei_activated)
Updates the stiffness matrix to account for the given edge being activated.
static void safelanes_destroyStacks(void)
Tears down the local faction/object stacks.
int safelanes_calculated(void)
Whether or not the safe lanes have been calculated at least once.
void safelanes_destroy(void)
Shuts down the safelanes system.
static double ** presence_budget
static Faction * faction_stack
static void safelanes_initStacks(void)
Sets up the local faction/object stacks.
static int safelanes_activateByGradient(cholmod_dense *Lambda_tilde, int iters_done)
Per-system, per-faction, activates the affordable lane with best (grad phi)/L.
static void safelanes_initStacks_anchor(void)
Identifies anchor points: The universe graph (with in-system and 2-way-jump edges) could have many co...
static void safelanes_initPPl(void)
Sets up the PPl matrices appearing in the gradient formula.
void safelanes_init(void)
Initializes the safelanes system.
static int vertex_faction(int vi)
Return the vertex's owning faction (ID, not faction_stack index), or -1 if not applicable.
static const vec2 * vertex_pos(int vi)
Return the vertex's coordinates within its system (by reference since our vec2's are fat).
static FactionMask MASK_ANY_FACTION()
Return a mask matching any faction.
static double safelanes_initialConductivity(int ei)
Returns the initial conductivity value (1/length) for edge ei. The live value is stored in the stiffn...
static cholmod_dense ** PPl
static void safelanes_initStacks_edge(void)
Sets up the local stacks with entry per edge. Faction stack must be set up.
static UnionFind tmp_sys_uf
static double safelanes_row_dot_row(cholmod_dense *A, cholmod_dense *B, int i, int j)
Return the i,j entry of A*B', or equivalently the dot product of row i of A with row j of B.
static int cmp_key(const void *p1, const void *p2)
It's a qsort comparator. Set the cmp_key_ref pointer prior to use, or else.
static cholmod_triplet * stiff
static int safelanes_buildOneTurn(int iters_done)
Run a round of optimization. Return how many builds (upper bound) have to happen next turn.
static const double LAMBDA
uint32_t FactionMask
A set of lane-building factions, represented as a bitfield.
static int * lane_faction
static const double JUMP_CONDUCTIVITY
void safelanes_recalculate(void)
Update the safe lane locations in response to the universe changing (e.g., diff applied).
static int * sys_to_first_edge
static void safelanes_initStacks_faction(void)
Sets up the local stacks with entry per faction.
static void safelanes_initOptimizer(void)
Initializes resources used by lane optimization.
static void safelanes_initStacks_vertex(void)
Sets up the local stacks with entry per vertex (or per jump).
static cholmod_sparse * QtQ
static void safelanes_initFTilde(void)
Sets up the fluxes matrix f~.
SafeLane * safelanes_get(int faction, int standing, const StarSystem *system)
Gets a set of safelanes for a faction and system.
static cholmod_dense * ncholmod_ddmult(cholmod_dense *A, int transA, cholmod_dense *B)
Dense times dense matrix. Return A*B, or A'*B if transA is true.
static int * sys_to_first_vertex
static FactionMask MASK_ONE_FACTION(int id)
A mask giving this faction (NOT faction_stack index) exclusive rights to build, if it's a lane-buildi...
static double * cmp_key_ref
static int safelanes_triangleTooFlat(const vec2 *m, const vec2 *n, const vec2 *p, double lmn)
Return true if this triangle is so flat that lanes from point m to point n aren't allowed.
static double * tmp_edge_conduct
int Edge[2]
An edge is a pair of vertex indices.
static int * tmp_anchor_vertices
static Vertex * vertex_stack
static void safelanes_destroyOptimizer(void)
Frees resources used by lane optimization.
static Edge * tmp_jump_edges
static int * tmp_spob_indices
static cholmod_dense * utilde
VertexType
Object type: like SafeLaneLocType, but with Naev stack indexing in mind.
static void safelanes_initQtQ(void)
Sets up the (Q*)Q matrix.
static int FACTION_ID_TO_INDEX(int id)
Return the faction_stack index corresponding to a faction ID, or -1.
StarSystem * system_getIndex(int id)
Get the system by its index.
StarSystem * system_getAll(void)
Gets an array (array.h) of all star systems.
double system_getPresence(const StarSystem *sys, int faction)
Get the presence of a faction in a system.
Description of a lane-building faction.
double lane_length_per_presence
Describes a safe lane, patrolled by a faction, within a system.
SafeLaneLocType point_type[2]
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Disjoint set forest on {0, .., n-1}.
Reference to a spob or jump point.
int * unionfind_findall(UnionFind *uf)
Returns a designated representative of each subset in an array (array.h).
int unionfind_find(UnionFind *uf, int x)
Finds the designated representative of the subset containing x.
void unionfind_init(UnionFind *uf, int n)
Creates a UnionFind structure on {0, ..., n}.
void unionfind_union(UnionFind *uf, int x, int y)
Declares x and y to be in the same subset.
void unionfind_free(UnionFind *uf)
Frees resources associated with uf.