14#if HAVE_SUITESPARSE_CHOLMOD_H
15#include <suitesparse/cholmod.h>
20#if HAVE_OPENBLAS_CBLAS_H
21#include <openblas/cblas.h>
22#elif HAVE_CBLAS_OPENBLAS_H
23#include <cblas_openblas.h>
24#elif HAVE_CBLAS_HYPHEN_OPENBLAS_H
25#include <cblas-openblas.h>
26#elif HAVE_ACCELERATE_ACCELERATE_H
27#include <Accelerate/Accelerate.h>
32#define I_LOVE_FORTRAN 1
33#elif HAVE_OPENBLAS_F77BLAS_H
34#include <openblas/f77blas.h>
35#define I_LOVE_FORTRAN 1
48#include "union_find.h"
53static const double ALPHA = 9.;
79typedef enum VertexType_ { VERTEX_SPOB, VERTEX_JUMP }
VertexType;
82typedef struct Vertex_ {
92typedef struct Faction_ {
106static cholmod_common
C;
140static cholmod_triplet
143static cholmod_sparse *
QtQ;
148static cholmod_dense **
PPl;
177 const vec2 *p,
double lmn );
184static int cmp_key(
const void *p1,
const void *p2 );
185static inline void triplet_entry( cholmod_triplet *m,
int i,
int j,
double v );
187 const double *sysPresence );
222 cholmod_finish( &
C );
248 if ( faction >= 0 ) {
249 if ( standing == 0 ) {
258 if ( ( standing & SAFELANES_FRIENDLY ) && fa )
260 if ( ( standing & SAFELANES_NEUTRAL ) && !fe )
262 if ( ( standing & SAFELANES_HOSTILE ) && fe )
269 for (
int j = 0; j < 2; j++ )
274 for (
int j = 0; j < 2; j++ ) {
275 switch ( v[j]->type ) {
278 l->
point_id[j] = system->spobs[v[j]->index]->id;
282 l->
point_id[j] = system->jumps[v[j]->index].targetid;
285 WARN( _(
"Safe-lane vertex type is invalid." ) );
299 Uint32 time = SDL_GetTicks();
315 DEBUG( n_(
"Charted safe lanes for %d object in %.3f s",
316 "Charted safe lanes for %d objects in %.3f s",
350 cholmod_free_dense( &
PPl[i], &
C );
353 cholmod_free_dense( &
utilde,
356 cholmod_free_dense( &
ftilde, &
C );
357 cholmod_free_sparse( &
QtQ, &
C );
358 cholmod_free_triplet( &
stiff, &
C );
367 cholmod_sparse *stiff_s;
368 cholmod_factor *stiff_f;
369 cholmod_dense *_QtQutilde, *Lambda_tilde, *Y_workspace, *E_workspace;
371 double zero[] = { 0, 0 }, neg_1[] = { -1, 0 };
373 Y_workspace = E_workspace = Lambda_tilde = NULL;
374 stiff_s = cholmod_triplet_to_sparse(
stiff, 0, &
C );
375 stiff_f = cholmod_analyze( stiff_s, &
C );
376 cholmod_factorize( stiff_s, stiff_f, &
C );
377 cholmod_solve2( CHOLMOD_A, stiff_f,
ftilde, NULL, &
utilde, NULL,
378 &Y_workspace, &E_workspace, &
C );
379 _QtQutilde = cholmod_zeros(
utilde->nrow,
utilde->ncol, CHOLMOD_REAL, &
C );
380 cholmod_sdmult(
QtQ, 0, neg_1, zero,
utilde, _QtQutilde, &
C );
381 cholmod_solve2( CHOLMOD_A, stiff_f, _QtQutilde, NULL, &Lambda_tilde, NULL,
382 &Y_workspace, &E_workspace, &
C );
383 cholmod_free_dense( &_QtQutilde, &
C );
384 cholmod_free_dense( &Y_workspace, &
C );
385 cholmod_free_dense( &E_workspace, &
C );
386 cholmod_free_factor( &stiff_f, &
C );
387 cholmod_free_sparse( &stiff_s, &
C );
389 cholmod_free_dense( &Lambda_tilde, &
C );
391 return turns_next_time;
420 if ( sys_isFlag( sys, SYSTEM_NOLANES ) ) {
425 for (
int i = 0; i <
array_size( sys->spobs ); i++ ) {
426 const Spob *p = sys->spobs[i];
427 if ( spob_isFlag( p, SPOB_NOLANES ) )
429 if ( p->presence.base != 0. || p->presence.bonus != 0. ) {
430 Vertex v = { .system = system, .type = VERTEX_SPOB, .index = i };
436 for (
int i = 0; i <
array_size( sys->jumps ); i++ ) {
437 const JumpPoint *jp = &sys->jumps[i];
438 if ( jp_isFlag( jp, JP_HIDDEN | JP_EXITONLY | JP_NOLANES ) )
440 Vertex v = { .system = system, .type = VERTEX_JUMP, .index = i };
442 if ( jp->targetid < system && jp->returnJump != NULL )
481 double lij = vec2_dist( pi, pj );
482 int has_approx_midpoint = 0;
485 if ( k != i && k != j &&
488 has_approx_midpoint = 1;
491 if ( has_approx_midpoint )
522 for (
int fi = 0; fi <
array_size( faction_all ); fi++ ) {
523 int f = faction_all[fi];
525 .lane_length_per_presence =
533 assert(
"FactionMask size is sufficient" &&
569 for (
int i = 0; i <
array_size( anchor_systems ); i++ )
627 double max_conductivity;
629 cholmod_free_triplet( &
stiff, &
C );
633 stiff = cholmod_allocate_triplet(
658 max_conductivity =
MAX(
667 assert( cholmod_check_triplet(
stiff, &
C ) );
679 double *sv =
stiff->x;
680 return lane_faction[ei] ? sv[3 * ei] / ( 1 + ALPHA ) : sv[3 * ei];
689 double *sv =
stiff->x;
690 for (
int i = 3 * ei_activated; i < 3 * ( ei_activated + 1 ); i++ )
701 cholmod_free_sparse( &
QtQ, &
C );
708 ( (
int *)Q->p )[0] = 0;
710 ( (
int *)Q->p )[i + 1] = 2 * ( i + 1 );
711 ( (
int *)Q->i )[2 * i + 0] =
edge_stack[i][0];
712 ( (
int *)Q->i )[2 * i + 1] =
edge_stack[i][1];
713 ( (
double *)Q->x )[2 * i + 0] = +1;
714 ( (
double *)Q->x )[2 * i + 1] = -1;
717 assert( cholmod_check_sparse( Q, &
C ) );
720 cholmod_free_sparse( &Q, &
C );
728 cholmod_sparse *eye =
734 cholmod_free_dense( &
ftilde, &
C );
735 ftilde = cholmod_sparse_to_dense( sp, &
C );
736 cholmod_free_sparse( &sp, &
C );
737 cholmod_free_sparse( &eye, &
C );
749 cholmod_free_dense( &
PPl[fi], &
C );
760 component = calloc( np,
sizeof(
int ) );
761 for (
int i = 0; i < np; i++ )
765 for (
int i = 0; i < np; i++ ) {
777 for (
int j = 0; j < i; j++ )
778 if ( component[i] == component[j] )
779 Di[np * i + j] += pres;
780 for (
int j = i + 1; j < np; j++ )
781 if ( component[i] == component[j] )
782 Di[np * j + i] += pres;
787 for (
int i = 0; i < np; i++ )
788 for (
int j = 0; j < i; j++ ) {
789 double d = ( (
double *)
PPl[fi]->x )[np * i + j];
791 ( (
double *)
PPl[fi]->x )[np * i + j] = -
d;
792 ( (
double *)
PPl[fi]->x )[np * j + i] = -
d;
793 ( (
double *)
PPl[fi]->x )[np * i + i] +=
d;
794 ( (
double *)
PPl[fi]->x )[np * j + j] +=
d;
808 int *facind_opts, *edgeind_opts, turns_next_time;
809 double *facind_vals, Linv;
812 size_t *lal_bases, lal_base;
838 double cost_best, cost_cheapest_other;
839 int fi = facind_opts[fii];
841 double score_best = 0.;
850 lal_base = lal_bases[fi];
862 int disconnecting = iters_done &&
877 if ( lal[fi] == NULL )
883 ei_best = edgeind_opts[0];
887 cost_cheapest_other = +HUGE_VAL;
891 for (
int eii = 0; eii <
array_size( edgeind_opts ); eii++ ) {
892 int ei = edgeind_opts[eii];
898 if ( lal[fi] == NULL ) {
903 cholmod_free_dense( &lamt, &
C );
911 sis - sys_base + lal_base );
913 sjs - sys_base + lal_base );
915 sis - sys_base + lal_base );
917 sjs - sys_base + lal_base );
919 score *= ALPHA * Linv * Linv;
925 if ( score < score_best ) {
928 cost_cheapest_other =
MIN( cost_cheapest_other, cost_best );
931 cost_cheapest_other =
MIN( cost_cheapest_other, cost );
936 if ( score_best >= 0. )
946 if ( lal[fi] == NULL )
959 if ( lal[fi] != NULL )
960 assert(
"Correctly tracked row offsets between the 'lal' and 'utilde' "
962 lal[fi]->nrow == lal_bases[fi] );
966 cholmod_free_dense( &lal[fi], &
C );
973 return turns_next_time;
978static int cmp_key(
const void *p1,
const void *p2 )
989 const vec2 *p,
double lmn )
991 const double MAX_COSINE = cos(
MIN_ANGLE );
992 double lnp = vec2_dist( n, p );
993 double lmp = vec2_dist( m, p );
994 double dpn = ( ( n->
x - m->
x ) * ( n->
x - p->x ) +
995 ( n->
y - m->
y ) * ( n->
y - p->y ) ) /
997 double dpm = ( ( m->
x - n->
x ) * ( m->
x - p->x ) +
998 ( m->
y - n->
y ) * ( m->
y - p->y ) ) /
1000 return ( dpn > MAX_COSINE && lnp < lmn ) ||
1001 ( dpm > MAX_COSINE && lmp < lmn );
1013 return sys->spobs[
vertex_stack[vi].index]->presence.faction;
1017 WARN( _(
"Safe-lane vertex type is invalid." ) );
1035 WARN( _(
"Safe-lane vertex type is invalid." ) );
1076static inline void triplet_entry( cholmod_triplet *m,
int i,
int j,
double x )
1078 ( (
int *)m->i )[m->nnz] = i;
1079 ( (
int *)m->j )[m->nnz] = j;
1080 ( (
double *)m->x )[m->nnz] = x;
1089 const double *sysPresence )
1091 size_t nr, nc, in_r, out_r;
1097 if ( sysPresence[si] > 0 )
1100 out = cholmod_allocate_dense( nr, nc, nr, CHOLMOD_REAL, &
C );
1105 if ( sysPresence[si] > 0 ) {
1106 for (
size_t c = 0;
c < nc;
c++ )
1107 memcpy( &( (
double *)out->x )[
c * out->d + out_r],
1108 &( (
double *)m->x )[
c * m->d + in_r],
1109 sz *
sizeof(
double ) );
1122 blasint M = transA ? A->ncol : A->nrow, K = transA ? A->nrow : A->ncol,
1123 N = B->ncol, lda = A->d, ldb = B->d, ldc = M;
1124 assert( K == (blasint)B->nrow );
1125 cholmod_dense *out = cholmod_allocate_dense( M, N, M, CHOLMOD_REAL, &
C );
1126 double alpha = 1., beta = 0.;
1128 ( transA ?
"T" :
"N",
"N", &M, &N, &K, &alpha, A->x, &lda, B->x, &ldb, &beta,
1131 size_t M = transA ? A->ncol : A->nrow, K = transA ? A->nrow : A->ncol,
1133 assert( K == B->nrow );
1134 cholmod_dense *out = cholmod_allocate_dense( M, N, M, CHOLMOD_REAL, &
C );
1135 cblas_dgemm( CblasColMajor, transA ? CblasTrans : CblasNoTrans, CblasNoTrans,
1136 M, N, K, 1, A->x, A->d, B->x, B->d, 0, out->x, out->d );
1146 assert( A->ncol == B->ncol );
1148 blasint N = A->ncol, incA = A->d, incB = B->d;
1149 return BLASFUNC( ddot )( &N, (
double *)A->x + i, &incA, (
double *)B->x + j,
1152 return cblas_ddot( A->ncol, (
double *)A->x + i, A->d, (
double *)B->x + j,
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
static Faction * faction_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.
static FactionMask * vertex_fmask
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 cholmod_dense * ftilde
static int safelanes_activateByGradient(const cholmod_dense *Lambda_tilde, int iters_done)
Per-system, per-faction, activates the affordable lane with best (grad phi)/L.
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.
@ STORAGE_MODE_UPPER_TRIANGULAR_PART
@ STORAGE_MODE_LOWER_TRIANGULAR_PART
@ STORAGE_MODE_UNSYMMETRIC
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 void safelanes_initStacks(void)
Sets up the local faction/object stacks.
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 double ** presence_budget
static cholmod_dense * safelanes_sliceByPresence(const cholmod_dense *m, const double *sysPresence)
Construct the matrix-slice of m, selecting those rows where the corresponding presence value is posit...
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.