naev 0.10.4
safelanes.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
11#include <math.h>
12
13#if HAVE_OPENBLAS_CBLAS_H
14# include <openblas/cblas.h>
15#elif HAVE_CBLAS_OPENBLAS_H
16# include <cblas_openblas.h>
17#elif HAVE_CBLAS_HYPHEN_OPENBLAS_H
18# include <cblas-openblas.h>
19#elif HAVE_ACCELERATE_ACCELERATE_H
20# include <Accelerate/Accelerate.h>
21#elif HAVE_CBLAS_H
22# include <cblas.h>
23#elif HAVE_F77BLAS_H
24# include <f77blas.h>
25# define I_LOVE_FORTRAN 1
26#endif
27
28#if HAVE_SUITESPARSE_CHOLMOD_H
29#include <suitesparse/cholmod.h>
30#else /* HAVE_SUITESPARSE_CHOLMOD_H */
31#include <cholmod.h>
32#endif /* HAVE_SUITESPARSE_CHOLMOD_H */
33
34#include "naev.h"
37#include "safelanes.h"
38
39#include "array.h"
40#include "conf.h"
41#include "log.h"
42#include "union_find.h"
43
44/*
45 * Global parameters.
46 */
47static const double ALPHA = 9.;
48static const double LAMBDA = 2e10;
49static const double JUMP_CONDUCTIVITY= 0.001;
50static const double MIN_ANGLE = M_PI/18.;
51enum {
55 SORTED = 1,
56 PACKED = 1,
58};
59
60/*
61 * Types.
62 */
66typedef enum VertexType_ {VERTEX_SPOB, VERTEX_JUMP} VertexType;
67
69typedef struct Vertex_ {
70 int system;
72 int index;
73} Vertex;
74
76typedef int Edge[2];
77
79typedef struct Faction_ {
80 int id;
81 double lane_length_per_presence;
82 double lane_base_cost;
83} Faction;
84
86typedef uint32_t FactionMask;
87static const FactionMask MASK_0 = 0, MASK_1 = 1;
88
89/*
90 * Global state.
91 */
92static cholmod_common C;
97static int *sys_to_first_edge;
99static int *lane_faction;
101static double **presence_budget;
102static int *tmp_spob_indices;
104static double *tmp_edge_conduct;
107static cholmod_triplet *stiff;
108static cholmod_sparse *QtQ;
109static cholmod_dense *ftilde;
110static cholmod_dense *utilde;
111static cholmod_dense **PPl;
112static double* cmp_key_ref;
115/*
116 * Prototypes.
117 */
118static int safelanes_buildOneTurn( int iters_done );
119static int safelanes_activateByGradient( cholmod_dense* Lambda_tilde, int iters_done );
120static void safelanes_initStacks (void);
121static void safelanes_initStacks_edge (void);
122static void safelanes_initStacks_faction (void);
123static void safelanes_initStacks_vertex (void);
124static void safelanes_initStacks_anchor (void);
125static void safelanes_initOptimizer (void);
126static void safelanes_destroyOptimizer (void);
127static void safelanes_destroyStacks (void);
128static void safelanes_destroyTmp (void);
129static void safelanes_initStiff (void);
130static double safelanes_initialConductivity ( int ei );
131static void safelanes_updateConductivity ( int ei_activated );
132static void safelanes_initQtQ (void);
133static void safelanes_initFTilde (void);
134static void safelanes_initPPl (void);
135static int safelanes_triangleTooFlat( const vec2* m, const vec2* n, const vec2* p, double lmn );
136static int vertex_faction( int vi );
137static const vec2* vertex_pos( int vi );
138static inline int FACTION_ID_TO_INDEX( int id );
139static inline FactionMask MASK_ANY_FACTION();
140static inline FactionMask MASK_ONE_FACTION( int id );
141static inline FactionMask MASK_COMPROMISE( int id1, int id2 );
142static int cmp_key( const void* p1, const void* p2 );
143static inline void triplet_entry( cholmod_triplet* m, int i, int j, double v );
144static cholmod_dense* safelanes_sliceByPresence( cholmod_dense* m, double* sysPresence );
145static cholmod_dense* ncholmod_ddmult( cholmod_dense* A, int transA, cholmod_dense* B );
146static double safelanes_row_dot_row( cholmod_dense* A, cholmod_dense* B, int i, int j );
147
151static inline void array_push_back_edge( Edge **a, int v0, int v1 )
152{
153 int *vs = array_grow( a );
154 vs[0] = v0;
155 vs[1] = v1;
156}
157
161void safelanes_init (void)
162{
163 cholmod_start( &C );
164 /* Ideally we would want to recalculate here, but since we load the first
165 * save and try to use unidiffs there, we instead defer the safe lane
166 * computation to only if necessary after loading save unidiffs. */
167 /* safelanes_recalculate(); */
168}
169
174{
177 cholmod_finish( &C );
178}
179
187SafeLane* safelanes_get( int faction, int standing, const StarSystem* system )
188{
190
191 for (int i=sys_to_first_edge[system->id]; i<sys_to_first_edge[1+system->id]; i++) {
192 SafeLane *l;
193 const Vertex *v[2];
194 int lf = lane_faction[i];
195
196 /* No lane on edge. */
197 if (lf <= 0)
198 continue;
199
200 /* Filter by standing. */
201 if (faction >= 0) {
202 if (standing==0) {
203 /* Only match exact faction. */
204 if (lf != faction)
205 continue;
206 }
207 else {
208 /* Try to do more advanced matching. */
209 int fe = areEnemies(faction,lf);
210 int fa = areAllies(faction,lf);
211 int skip = 1;
212 if ((standing & SAFELANES_FRIENDLY) && fa)
213 skip = 0;
214 if ((standing & SAFELANES_NEUTRAL) && !fe)
215 skip = 0;
216 if ((standing & SAFELANES_HOSTILE) && fe)
217 skip = 0;
218 if (skip)
219 continue;
220 }
221 }
222
223 for (int j=0; j<2; j++)
224 v[j] = &vertex_stack[edge_stack[i][j]];
225
226 l = &array_grow( &out );
227 l->faction = lane_faction[i];
228 for (int j=0; j<2; j++) {
229 switch (v[j]->type) {
230 case VERTEX_SPOB:
231 l->point_type[j] = SAFELANE_LOC_SPOB;
232 l->point_id[j] = system->spobs[v[j]->index]->id;
233 break;
234 case VERTEX_JUMP:
235 l->point_type[j] = SAFELANE_LOC_DEST_SYS;
236 l->point_id[j] = system->jumps[v[j]->index].targetid;
237 break;
238 default:
239 ERR( _("Safe-lane vertex type is invalid.") );
240 }
241 }
242 }
243 return out;
244}
245
250{
251 Uint32 time = SDL_GetTicks();
252
253 /* Don't recompute on exit. */
254 if (naev_isQuit())
255 return;
256
259 for (int iters_done=0; safelanes_buildOneTurn(iters_done) > 0; iters_done++)
260 ;
262 /* Stacks remain available for queries. */
263 time = SDL_GetTicks() - time;
264 if (conf.devmode)
265 DEBUG( n_("Charted safe lanes for %d object in %.3f s", "Charted safe lanes for %d objects in %.3f s", array_size(vertex_stack)), array_size(vertex_stack), time/1000. );
266
268}
269
274{
276}
277
281static void safelanes_initOptimizer (void)
282{
288}
289
294{
295 for (int i=0; i<array_size(PPl); i++)
296 cholmod_free_dense( &PPl[i], &C );
297 array_free( PPl );
298 PPl = NULL;
299 cholmod_free_dense( &utilde, &C ); /* CAUTION: if we instead save it, ensure it's updated after the final activateByGradient. */
300 cholmod_free_dense( &ftilde, &C );
301 cholmod_free_sparse( &QtQ, &C );
302 cholmod_free_triplet( &stiff, &C );
303}
304
308static int safelanes_buildOneTurn( int iters_done )
309{
310 cholmod_sparse *stiff_s;
311 cholmod_factor *stiff_f;
312 cholmod_dense *_QtQutilde, *Lambda_tilde, *Y_workspace, *E_workspace;
313 int turns_next_time;
314 double zero[] = {0, 0}, neg_1[] = {-1, 0};
315
316 Y_workspace = E_workspace = Lambda_tilde = NULL;
317 stiff_s = cholmod_triplet_to_sparse( stiff, 0, &C );
318 stiff_f = cholmod_analyze( stiff_s, &C );
319 cholmod_factorize( stiff_s, stiff_f, &C );
320 cholmod_solve2( CHOLMOD_A, stiff_f, ftilde, NULL, &utilde, NULL, &Y_workspace, &E_workspace, &C );
321 _QtQutilde = cholmod_zeros( utilde->nrow, utilde->ncol, CHOLMOD_REAL, &C );
322 cholmod_sdmult( QtQ, 0, neg_1, zero, utilde, _QtQutilde, &C );
323 cholmod_solve2( CHOLMOD_A, stiff_f, _QtQutilde, NULL, &Lambda_tilde, NULL, &Y_workspace, &E_workspace, &C );
324 cholmod_free_dense( &_QtQutilde, &C );
325 cholmod_free_dense( &Y_workspace, &C );
326 cholmod_free_dense( &E_workspace, &C );
327 cholmod_free_factor( &stiff_f, &C );
328 cholmod_free_sparse( &stiff_s, &C );
329 turns_next_time = safelanes_activateByGradient( Lambda_tilde, iters_done );
330 cholmod_free_dense( &Lambda_tilde, &C );
331
332 return turns_next_time;
333}
334
338static void safelanes_initStacks (void)
339{
341 safelanes_initStacks_faction(); /* Dependency for vertex. */
342 safelanes_initStacks_vertex(); /* Dependency for edge. */
345}
346
351{
352 const StarSystem *systems_stack = system_getAll();
353
359 for (int system=0; system<array_size(systems_stack); system++) {
360 const StarSystem *sys = &systems_stack[system];
361
362 if (sys_isFlag( sys, SYSTEM_NOLANES ))
363 continue;
364
365 for (int i=0; i<array_size(sys->spobs); i++) {
366 const Spob *p = sys->spobs[i];
367 if (p->presence.base!=0. || p->presence.bonus!=0.) {
368 Vertex v = {.system = system, .type = VERTEX_SPOB, .index = i};
371 }
372 }
373
374 for (int i=0; i<array_size(sys->jumps); i++) {
375 const JumpPoint *jp = &sys->jumps[i];
376 if (!jp_isFlag( jp, JP_HIDDEN | JP_EXITONLY )) {
377 Vertex v = {.system = system, .type = VERTEX_JUMP, .index = i};
379 if (jp->targetid < system && jp->returnJump != NULL)
380 for (int j=sys_to_first_vertex[jp->targetid]; j < sys_to_first_vertex[1+jp->targetid]; j++)
381 if (vertex_stack[j].type == VERTEX_JUMP && jp->returnJump == &jp->target->jumps[vertex_stack[j].index]) {
383 break;
384 }
385 }
386 }
388 }
391
392 vertex_fmask = calloc( array_size(vertex_stack), sizeof(FactionMask) );
393}
394
399{
400 const StarSystem *systems_stack = system_getAll();
401
406 tmp_edge_conduct = array_create( double );
407 for (int system=0; system<array_size(systems_stack); system++) {
408 for (int i=sys_to_first_vertex[system]; i < sys_to_first_vertex[1+system]; i++) {
409 const vec2 *pi = vertex_pos( i );
410 for (int j=sys_to_first_vertex[system]; j < i; j++) {
411 const vec2 *pj = vertex_pos( j );
412 double lij = vec2_dist( pi, pj );
413 int has_approx_midpoint = 0;
414 for (int k=sys_to_first_vertex[system]; k < sys_to_first_vertex[1+system]; k++)
415 if (k!=i && k!=j && safelanes_triangleTooFlat( pi, pj, vertex_pos( k ), lij )) {
416 has_approx_midpoint = 1;
417 break;
418 }
419 if (has_approx_midpoint)
420 continue; /* The edge from i to j is disallowed in favor of a path through k. */
424 }
425 }
427 }
430
433 memset( lane_faction, 0, array_size(lane_faction)*sizeof(lane_faction[0]) );
434}
435
440{
441 int *faction_all;
442 const StarSystem *systems_stack;
443
445 faction_all = faction_getAllVisible();
446 for (int fi=0; fi<array_size(faction_all); fi++) {
447 int f = faction_all[fi];
448 Faction rec = {.id = f, .lane_length_per_presence = faction_lane_length_per_presence(f), .lane_base_cost = faction_lane_base_cost(f)};
449 if (rec.lane_length_per_presence > 0.)
451 }
452 array_free( faction_all );
454 assert( "FactionMask size is sufficient" && (size_t)array_size(faction_stack) <= 8*sizeof(FactionMask) );
455
458 for (int fi=0; fi<array_size(faction_stack); fi++) {
460 for (int s=0; s<array_size(systems_stack); s++)
462 }
463}
464
471{
472 int *anchor_systems;
473 int nsys = array_size(sys_to_first_vertex) - 1;
474 unionfind_init( &tmp_sys_uf, nsys );
475 for (int i=0; i<array_size(tmp_jump_edges); i++)
477 anchor_systems = unionfind_findall( &tmp_sys_uf );
478 tmp_anchor_vertices = array_create_size( int, array_size(anchor_systems) );
479
480 /* Add an anchor vertex per system, but only if there actually is a vertex in the system. */
481 for (int i=0; i<array_size(anchor_systems); i++)
482 if (sys_to_first_vertex[anchor_systems[i]] < sys_to_first_vertex[1+anchor_systems[i]])
484 array_free( anchor_systems );
485}
486
490static void safelanes_destroyStacks (void)
491{
494 vertex_stack = NULL;
495 free( vertex_fmask );
496 vertex_fmask = NULL;
498 sys_to_first_vertex = NULL;
500 edge_stack = NULL;
502 sys_to_first_edge = NULL;
503 for (int i=0; i<array_size(presence_budget); i++)
506 presence_budget = NULL;
508 faction_stack = NULL;
510 lane_faction = NULL;
512 lane_fmask = NULL;
513}
514
518static void safelanes_destroyTmp (void)
519{
522 tmp_spob_indices = NULL;
524 tmp_jump_edges = NULL;
526 tmp_edge_conduct = NULL;
528 tmp_anchor_vertices = NULL;
529}
530
534static void safelanes_initStiff (void)
535{
536 int nnz, v;
537 double max_conductivity;
538
539 cholmod_free_triplet( &stiff, &C );
542 stiff = cholmod_allocate_triplet( v, v, nnz, STORAGE_MODE_UPPER_TRIANGULAR_PART, CHOLMOD_REAL, &C );
543 /* Populate triplets: internal edges (ii ij jj), implicit jump connections (ditto), anchor conditions. */
544 for (int i=0; i<array_size(edge_stack); i++) {
545 triplet_entry( stiff, edge_stack[i][0], edge_stack[i][0], +tmp_edge_conduct[i] );
546 triplet_entry( stiff, edge_stack[i][0], edge_stack[i][1], -tmp_edge_conduct[i] );
547 triplet_entry( stiff, edge_stack[i][1], edge_stack[i][1], +tmp_edge_conduct[i] );
548 }
549 for (int i=0; i<array_size(tmp_jump_edges); i++) {
550 triplet_entry( stiff, tmp_jump_edges[i][0], tmp_jump_edges[i][0], +JUMP_CONDUCTIVITY );
551 triplet_entry( stiff, tmp_jump_edges[i][0], tmp_jump_edges[i][1], -JUMP_CONDUCTIVITY );
552 triplet_entry( stiff, tmp_jump_edges[i][1], tmp_jump_edges[i][1], +JUMP_CONDUCTIVITY );
553 }
554 /* Add a Robin boundary condition, using the max conductivity (after activation) for spectral reasons. */
555 max_conductivity = JUMP_CONDUCTIVITY/(1+ALPHA);
556 for (int i=0; i<array_size(edge_stack); i++)
557 max_conductivity = MAX( max_conductivity, tmp_edge_conduct[i] );
558 max_conductivity = MAX( JUMP_CONDUCTIVITY, (1+ALPHA)*max_conductivity ); /* Activation scales entries by 1+ALPHA later. */
559 for (int i=0; i<array_size(tmp_anchor_vertices); i++)
560 triplet_entry( stiff, tmp_anchor_vertices[i], tmp_anchor_vertices[i], max_conductivity );
561#if DEBUGGING
562 assert( stiff->nnz == stiff->nzmax );
563 assert( cholmod_check_triplet( stiff, &C) );
564#endif /* DEBUGGING */
565}
566
572static double safelanes_initialConductivity ( int ei )
573{
574 double *sv = stiff->x;
575 return lane_faction[ei] ? sv[3*ei]/(1+ALPHA) : sv[3*ei];
576}
577
582static void safelanes_updateConductivity ( int ei_activated )
583{
584 double *sv = stiff->x;
585 for (int i=3*ei_activated; i<3*(ei_activated+1); i++)
586 sv[i] *= 1+ALPHA;
587}
588
592static void safelanes_initQtQ (void)
593{
594 cholmod_sparse *Q;
595
596 cholmod_free_sparse( &QtQ, &C );
597 /* Form Q, the edge-vertex projection where (Dirac notation) Q |edge> = |edge[0]> - |edge[1]>. It has a +1 and -1 per column. */
598 Q = cholmod_allocate_sparse( array_size(vertex_stack), array_size(edge_stack), 2*array_size(edge_stack),
599 SORTED, PACKED, STORAGE_MODE_UNSYMMETRIC, CHOLMOD_REAL, &C );
600 ((int*)Q->p)[0] = 0;
601 for (int i=0; i<array_size(edge_stack); i++) {
602 ((int*)Q->p)[i+1] = 2*(i+1);
603 ((int*)Q->i)[2*i+0] = edge_stack[i][0];
604 ((int*)Q->i)[2*i+1] = edge_stack[i][1];
605 ((double*)Q->x)[2*i+0] = +1;
606 ((double*)Q->x)[2*i+1] = -1;
607 }
608#if DEBUGGING
609 assert( cholmod_check_sparse( Q, &C ) );
610#endif /* DEBUGGING */
611 QtQ = cholmod_aat( Q, NULL, 0, MODE_NUMERICAL, &C );
612 cholmod_free_sparse( &Q, &C );
613}
614
618static void safelanes_initFTilde (void)
619{
620 cholmod_sparse *eye = cholmod_speye( array_size(vertex_stack), array_size(vertex_stack), CHOLMOD_REAL, &C );
621 cholmod_sparse *sp = cholmod_submatrix( eye, NULL, -1, tmp_spob_indices, array_size(tmp_spob_indices), 1, SORTED, &C );
622 cholmod_free_dense( &ftilde, &C );
623 ftilde = cholmod_sparse_to_dense( sp, &C );
624 cholmod_free_sparse( &sp, &C );
625 cholmod_free_sparse( &eye, &C );
626}
627
631static void safelanes_initPPl (void)
632{
633 int *component;
635
636 for (int fi=0; fi<array_size(PPl); fi++)
637 cholmod_free_dense( &PPl[fi], &C );
638 array_free( PPl );
639 PPl = array_create_size( cholmod_dense*, array_size(faction_stack) );
640 for (int fi=0; fi<array_size(faction_stack); fi++)
641 array_push_back( &PPl, cholmod_zeros( np, np, CHOLMOD_REAL, &C ) );
642
643 /* Form P, the pair-vertex projection where (Dirac notation) P |pair(i,j)> = |i> - |j>. It has a +1 and -1 per column. */
644 /* At least, pretend we did. We want (PD)(PD)*, where D is a diagonal matrix whose pair(i,j) are these presence sums: */
645
646 component = calloc( np, sizeof(int) );
647 for (int i=0; i<np; i++)
648 component[i] = unionfind_find( &tmp_sys_uf, vertex_stack[tmp_spob_indices[i]].system );
649
650 for (int i=0; i<np; i++) {
651 double *Di;
653 Spob *pnt = system_getIndex( sys )->spobs[vertex_stack[tmp_spob_indices[i]].index];
654 double pres = pnt->presence.base + pnt->presence.bonus; /* TODO distinguish between base and bonus? */
655 int fi = FACTION_ID_TO_INDEX( pnt->presence.faction );
656 if (fi < 0)
657 continue;
658 Di = PPl[fi]->x;
659 for (int j=0; j<i; j++)
660 if (component[i] == component[j])
661 Di[np*i+j] += pres;
662 for (int j=i+1; j<np; j++)
663 if (component[i] == component[j])
664 Di[np*j+i] += pres;
665 }
666
667 /* At this point, PPl[fi]->x[np*i+j] holds the pair(i,j) entry of D. */
668 for (int fi=0; fi<array_size(faction_stack); fi++)
669 for (int i=0; i<np; i++)
670 for (int j=0; j<i; j++) {
671 double d = ((double*)PPl[fi]->x)[np*i+j];
672 d *= d;
673 ((double*)PPl[fi]->x)[np*i+j] = -d;
674 ((double*)PPl[fi]->x)[np*j+i] = -d;
675 ((double*)PPl[fi]->x)[np*i+i] += d;
676 ((double*)PPl[fi]->x)[np*j+j] += d;
677 }
678
679 free( component );
680}
681
686static int safelanes_activateByGradient( cholmod_dense* Lambda_tilde, int iters_done )
687{
688 int *facind_opts, *edgeind_opts, turns_next_time;
689 double *facind_vals, Linv;
690 cholmod_dense **lal;
691 size_t *lal_bases, lal_base;
693 lal = calloc( array_size(faction_stack), sizeof(cholmod_dense*) );
694 lal_bases = calloc( array_size(faction_stack), sizeof(size_t) );
695 edgeind_opts = array_create( int );
696 facind_opts = array_create_size( int, array_size(faction_stack) );
697 facind_vals = array_create_size( double, array_size(faction_stack) );
698 for (int fi=0; fi<array_size(faction_stack); fi++) {
699 array_push_back( &facind_opts, fi );
700 array_push_back( &facind_vals, 0 );
701 }
702 turns_next_time = 0;
703
704 for (int si=0; si<array_size(sys_to_first_vertex)-1; si++) {
705 /* Factions with most presence here choose first. */
706 StarSystem *sys = system_getIndex( si );
707 for (int fi=0; fi<array_size(faction_stack); fi++)
708 facind_vals[fi] = -system_getPresence( sys, faction_stack[fi].id ); /* FIXME: Is this better, or presence_budget? */
709 cmp_key_ref = facind_vals;
710 qsort( facind_opts, array_size(faction_stack), sizeof(int), cmp_key );
711
712 for (int fii=0; fii<array_size(faction_stack); fii++) {
713 int ei_best;
714 double cost_best, cost_cheapest_other;
715 int fi = facind_opts[fii];
716 size_t sys_base = sys_to_first_vertex[si];
717 double score_best = 0.; /* Negative scores get ignored. */
718
719 /* Get the base index to use for this system. Save the value we expect to be the next iteration's base index.
720 * The current system's rows are in the lal[fi] matrix if there's presence at the time we slice it.
721 * What we know is whether there's presence *now*. We can use that as a proxy and fix lal_bases[fi] if we
722 * deplete this presence before constructing lal[fi]. This is tricky, so there are assertions below,
723 * which can warn us if we fuck this up. */
724 lal_base = lal_bases[fi];
725 if (presence_budget[fi][si] <= 0.)
726 continue;
727 /* We "should" find these DoF's interesting if/when we slice, and will unless we deplete this presence first. */
728 lal_bases[fi] += sys_to_first_vertex[1+si] - sys_to_first_vertex[si];
729
730 array_resize( &edgeind_opts, 0 );
731 for (int ei=sys_to_first_edge[si]; ei<sys_to_first_edge[1+si]; ei++) {
732 int sis = edge_stack[ei][0];
733 int sjs = edge_stack[ei][1];
734 int disconnecting = iters_done && !(vertex_fmask[sis] & (MASK_1<<fi)) && !(vertex_fmask[sjs] & (MASK_1<<fi));
736 if (!lane_faction[ei]
737 && !disconnecting
738 && presence_budget[fi][si] >= cost
739 && (lane_fmask[ei] & (MASK_1<<fi)))
740 array_push_back( &edgeind_opts, ei );
741 }
742
743 if (array_size(edgeind_opts) == 0) {
744 presence_budget[fi][si] = 0.; /* Nothing to build here! Tell ourselves to stop trying. */
745 if (lal[fi] == NULL)
746 lal_bases[fi] -= sys_to_first_vertex[1+si] - sys_to_first_vertex[si];
747 continue;
748 }
749
750 ei_best = edgeind_opts[0];
752 cost_cheapest_other = +HUGE_VAL;
753 if (array_size(edgeind_opts) > 0) {
754 /* There's an actual choice. Search for the best option. Lower is better. */
755 for (int eii=0; eii<array_size(edgeind_opts); eii++) {
756 int ei = edgeind_opts[eii];
757 int sis = edge_stack[ei][0];
758 int sjs = edge_stack[ei][1];
759 double score = 0.;
760 double cost;
761
762 if (lal[fi] == NULL) { /* Is it time to evaluate the lazily-calculated matrix? */
763 cholmod_dense *lamt = safelanes_sliceByPresence( Lambda_tilde, presence_budget[fi] );
764 lal[fi] = ncholmod_ddmult( lamt, 0, PPl[fi] );
765 cholmod_free_dense( &lamt, &C );
766 }
767
768 /* Evaluate (LUTll[0,0] + LUTll[1,1] - LUTll[0,1] - LUTll[1,0]), */
769 /* where LUTll = np.dot( lal[[sis,sjs],:] , utilde[[sis,sjs],:].T ) */
770 score += safelanes_row_dot_row( utilde, lal[fi], sis, sis - sys_base + lal_base );
771 score += safelanes_row_dot_row( utilde, lal[fi], sjs, sjs - sys_base + lal_base );
772 score -= safelanes_row_dot_row( utilde, lal[fi], sjs, sis - sys_base + lal_base );
773 score -= safelanes_row_dot_row( utilde, lal[fi], sis, sjs - sys_base + lal_base );
775 score *= ALPHA * Linv * Linv;
776 score += LAMBDA;
777
779 if (score < score_best) {
780 ei_best = ei;
781 score_best = score;
782 cost_cheapest_other = MIN( cost_cheapest_other, cost_best );
783 cost_best = cost;
784 }
785 else
786 cost_cheapest_other = MIN( cost_cheapest_other, cost );
787 }
788 }
789
790 /* Ignore positive scores. */
791 if (score_best >= 0.)
792 continue;
793
794 /* Add the lane. */
795 presence_budget[fi][si] -= cost_best;
796 if (presence_budget[fi][si] >= cost_cheapest_other)
797 turns_next_time++;
798 else {
799 presence_budget[fi][si] = 0.; /* Nothing more to do here; tell ourselves. */
800 if (lal[fi] == NULL)
801 lal_bases[fi] -= sys_to_first_vertex[1+si] - sys_to_first_vertex[si];
802 }
804 vertex_fmask[edge_stack[ei_best][0]] |= (MASK_1<<fi);
805 vertex_fmask[edge_stack[ei_best][1]] |= (MASK_1<<fi);
806 lane_faction[ ei_best ] = faction_stack[fi].id;
807 }
808 }
809
810#if DEBUGGING
811 for (int fi=0; fi<array_size(faction_stack); fi++)
812 if (lal[fi] != NULL)
813 assert( "Correctly tracked row offsets between the 'lal' and 'utilde' matrices" && lal[fi]->nrow == lal_bases[fi] );
814#endif /* DEBUGGING */
815
816 for (int fi=0; fi<array_size(faction_stack); fi++)
817 cholmod_free_dense( &lal[fi], &C );
818 free( lal );
819 free( lal_bases );
820 array_free( edgeind_opts );
821 array_free( facind_vals );
822 array_free( facind_opts );
823
824 return turns_next_time;
825}
826
828static int cmp_key( const void* p1, const void* p2 )
829{
830 double d = cmp_key_ref[*(int*)p1] - cmp_key_ref[*(int*)p2];
831 return SIGN( d );
832}
833
837static int safelanes_triangleTooFlat( const vec2* m, const vec2* n, const vec2* p, double lmn )
838{
839 const double MAX_COSINE = cos(MIN_ANGLE);
840 double lnp = vec2_dist( n, p );
841 double lmp = vec2_dist( m, p );
842 double dpn = ((n->x-m->x)*(n->x-p->x) + (n->y-m->y)*(n->y-p->y)) / ( lmn * lnp );
843 double dpm = ((m->x-n->x)*(m->x-p->x) + (m->y-n->y)*(m->y-p->y)) / ( lmn * lmp );
844 return (dpn > MAX_COSINE && lnp < lmn) || (dpm > MAX_COSINE && lmp < lmn);
845}
846
850static int vertex_faction( int vi )
851{
852 const StarSystem *sys = system_getIndex(vertex_stack[vi].system);
853 switch (vertex_stack[vi].type) {
854 case VERTEX_SPOB:
855 return sys->spobs[vertex_stack[vi].index]->presence.faction;
856 case VERTEX_JUMP:
857 return -1;
858 default:
859 ERR( _("Safe-lane vertex type is invalid.") );
860 }
861}
862
866static const vec2* vertex_pos( int vi )
867{
868 const StarSystem *sys = system_getIndex(vertex_stack[vi].system);
869 switch (vertex_stack[vi].type) {
870 case VERTEX_SPOB:
871 return &sys->spobs[vertex_stack[vi].index]->pos;
872 case VERTEX_JUMP:
873 return &sys->jumps[vertex_stack[vi].index].pos;
874 default:
875 ERR( _("Safe-lane vertex type is invalid.") );
876 }
877}
878
880static inline int FACTION_ID_TO_INDEX( int id )
881{
882 for (int i=0; i<array_size(faction_stack) && faction_stack[i].id <= id; i++)
883 if (faction_stack[i].id == id)
884 return i;
885 return -1;
886}
887
890{
891 return ~MASK_0;
892}
893
895static inline FactionMask MASK_ONE_FACTION( int id )
896{
897 int ind = FACTION_ID_TO_INDEX( id );
898 return ind>0 ? (MASK_1)<<ind : MASK_ANY_FACTION();
899}
900
902static inline FactionMask MASK_COMPROMISE( int id1, int id2 )
903{
904 FactionMask m1 = MASK_ONE_FACTION(id1), m2 = MASK_ONE_FACTION(id2);
905 return (m1 & m2) ? (m1 & m2) : (m1 | m2); /* Any/Any -> any, Any/f -> just f, f1/f2 -> either. */
906}
907
908static inline void triplet_entry( cholmod_triplet* m, int i, int j, double x )
909{
910 ((int*)m->i)[m->nnz] = i;
911 ((int*)m->j)[m->nnz] = j;
912 ((double*)m->x)[m->nnz] = x;
913 m->nnz++;
914}
915
919static cholmod_dense* safelanes_sliceByPresence( cholmod_dense* m, double* sysPresence )
920{
921 size_t nr, nc, in_r, out_r;
922 cholmod_dense *out;
923
924 nr = 0;
925 nc = m->ncol;
926 for (int si=0; si < array_size(sys_to_first_vertex) - 1; si++)
927 if (sysPresence[si] > 0)
929
930 out = cholmod_allocate_dense( nr, nc, nr, CHOLMOD_REAL, &C );
931
932 in_r = out_r = 0;
933 for (int si=0; si < array_size(sys_to_first_vertex) - 1; si++) {
934 int sz = sys_to_first_vertex[1+si] - sys_to_first_vertex[si];
935 if (sysPresence[si] > 0) {
936 for (size_t c = 0; c < nc; c++)
937 memcpy( &((double*)out->x)[c*out->d + out_r], &((double*)m->x)[c*m->d + in_r], sz * sizeof(double) );
938 out_r += sz;
939 }
940 in_r += sz;
941 }
942 return out;
943}
944
946static cholmod_dense* ncholmod_ddmult( cholmod_dense* A, int transA, cholmod_dense* B )
947{
948#if I_LOVE_FORTRAN
949 blasint M = transA ? A->ncol : A->nrow, K = transA ? A->nrow : A->ncol, N = B->ncol, lda = A->d, ldb = B->d, ldc = M;
950 assert( K == (blasint) B->nrow );
951 cholmod_dense *out = cholmod_allocate_dense( M, N, M, CHOLMOD_REAL, &C );
952 double alpha = 1., beta = 0.;
953 BLASFUNC(dgemm)( transA ? "T" : "N", "N", &M, &N, &K, &alpha, A->x, &lda, B->x, &ldb, &beta, out->x, &ldc);
954#else /* I_LOVE_FORTRAN */
955 size_t M = transA ? A->ncol : A->nrow, K = transA ? A->nrow : A->ncol, N = B->ncol;
956 assert( K == B->nrow );
957 cholmod_dense *out = cholmod_allocate_dense( M, N, M, CHOLMOD_REAL, &C );
958 cblas_dgemm( CblasColMajor, transA?CblasTrans:CblasNoTrans, CblasNoTrans, M, N, K, 1, A->x, A->d, B->x, B->d, 0, out->x, out->d);
959#endif /* I_LOVE_FORTRAN */
960 return out;
961}
962
964static double safelanes_row_dot_row( cholmod_dense* A, cholmod_dense* B, int i, int j )
965{
966 assert( A->ncol == B->ncol );
967#if I_LOVE_FORTRAN
968 blasint N = A->ncol, incA = A->d, incB = B->d;
969 return BLASFUNC(ddot)( &N, (double*)A->x + i, &incA, (double*)B->x + j, &incB);
970#else /* I_LOVE_FORTRAN */
971 return cblas_ddot( A->ncol, (double*)A->x + i, A->d, (double*)B->x + j, B->d);
972#endif /* I_LOVE_FORTRAN */
973}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition: array.h:158
#define array_resize(ptr_array, new_size)
Resizes the array to accomodate new_size elements.
Definition: array.h:112
#define array_create_size(basic_type, capacity)
Creates a new dynamic array of ‘basic_type’ with an initial capacity.
Definition: array.h:102
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition: array.h:168
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition: array.h:119
#define array_shrink(ptr_array)
Shrinks memory to fit only ‘size’ elements.
Definition: array.h:149
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition: array.h:129
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition: array.h:93
StarSystem * systems_stack
Definition: space.c:92
int areEnemies(int a, int b)
Checks whether two factions are enemies.
Definition: faction.c:1197
double faction_lane_base_cost(int f)
Gets the faction's weight for patrolled safe-lane construction;.
Definition: faction.c:436
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).
Definition: faction.c:424
int * faction_getAllVisible(void)
Returns all non-invisible faction IDs in an array (array.h).
Definition: faction.c:205
int areAllies(int a, int b)
Checks whether two factions are allies or not.
Definition: faction.c:1222
int naev_isQuit(void)
Get if Naev is trying to quit.
Definition: naev.c:155
Header file with generic functions and naev-specifics.
#define MIN(x, y)
Definition: naev.h:40
#define SIGN(x)
Definition: naev.h:43
#define MAX(x, y)
Definition: naev.h:39
static const double c[]
Definition: rng.c:264
static const double d[]
Definition: rng.c:273
static int safelanes_calculated_once
Definition: safelanes.c:113
static FactionMask MASK_COMPROMISE(int id1, int id2)
A mask with appropriate lane-building rights given one faction ID owning each endpoint.
Definition: safelanes.c:902
static FactionMask * vertex_fmask
Definition: safelanes.c:94
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...
Definition: safelanes.c:919
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.
Definition: safelanes.c:151
static FactionMask * lane_fmask
Definition: safelanes.c:100
static const double MIN_ANGLE
Definition: safelanes.c:50
static void safelanes_initStiff(void)
Sets up the stiffness matrix.
Definition: safelanes.c:534
static void safelanes_destroyTmp(void)
Tears down the local faction/object stacks.
Definition: safelanes.c:518
static const double ALPHA
Definition: safelanes.c:47
static cholmod_dense * ftilde
Definition: safelanes.c:109
static void safelanes_updateConductivity(int ei_activated)
Updates the stiffness matrix to account for the given edge being activated.
Definition: safelanes.c:582
static void safelanes_destroyStacks(void)
Tears down the local faction/object stacks.
Definition: safelanes.c:490
int safelanes_calculated(void)
Whether or not the safe lanes have been calculated at least once.
Definition: safelanes.c:273
void safelanes_destroy(void)
Shuts down the safelanes system.
Definition: safelanes.c:173
static double ** presence_budget
Definition: safelanes.c:101
static Faction * faction_stack
Definition: safelanes.c:98
static Edge * edge_stack
Definition: safelanes.c:96
static void safelanes_initStacks(void)
Sets up the local faction/object stacks.
Definition: safelanes.c:338
static int safelanes_activateByGradient(cholmod_dense *Lambda_tilde, int iters_done)
Per-system, per-faction, activates the affordable lane with best (grad phi)/L.
Definition: safelanes.c:686
static void safelanes_initStacks_anchor(void)
Identifies anchor points: The universe graph (with in-system and 2-way-jump edges) could have many co...
Definition: safelanes.c:470
static void safelanes_initPPl(void)
Sets up the PPl matrices appearing in the gradient formula.
Definition: safelanes.c:631
void safelanes_init(void)
Initializes the safelanes system.
Definition: safelanes.c:161
static int vertex_faction(int vi)
Return the vertex's owning faction (ID, not faction_stack index), or -1 if not applicable.
Definition: safelanes.c:850
static const vec2 * vertex_pos(int vi)
Return the vertex's coordinates within its system (by reference since our vec2's are fat).
Definition: safelanes.c:866
static FactionMask MASK_ANY_FACTION()
Return a mask matching any faction.
Definition: safelanes.c:889
static double safelanes_initialConductivity(int ei)
Returns the initial conductivity value (1/length) for edge ei. The live value is stored in the stiffn...
Definition: safelanes.c:572
static cholmod_dense ** PPl
Definition: safelanes.c:111
static void safelanes_initStacks_edge(void)
Sets up the local stacks with entry per edge. Faction stack must be set up.
Definition: safelanes.c:398
static UnionFind tmp_sys_uf
Definition: safelanes.c:106
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.
Definition: safelanes.c:964
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.
Definition: safelanes.c:828
static cholmod_triplet * stiff
Definition: safelanes.c:107
static int safelanes_buildOneTurn(int iters_done)
Run a round of optimization. Return how many builds (upper bound) have to happen next turn.
Definition: safelanes.c:308
static const double LAMBDA
Definition: safelanes.c:48
uint32_t FactionMask
A set of lane-building factions, represented as a bitfield.
Definition: safelanes.c:86
static int * lane_faction
Definition: safelanes.c:99
static const double JUMP_CONDUCTIVITY
Definition: safelanes.c:49
void safelanes_recalculate(void)
Update the safe lane locations in response to the universe changing (e.g., diff applied).
Definition: safelanes.c:249
static int * sys_to_first_edge
Definition: safelanes.c:97
static void safelanes_initStacks_faction(void)
Sets up the local stacks with entry per faction.
Definition: safelanes.c:439
static cholmod_common C
Definition: safelanes.c:92
@ STORAGE_MODE_UPPER_TRIANGULAR_PART
Definition: safelanes.c:54
@ PACKED
Definition: safelanes.c:56
@ SORTED
Definition: safelanes.c:55
@ MODE_NUMERICAL
Definition: safelanes.c:57
@ STORAGE_MODE_LOWER_TRIANGULAR_PART
Definition: safelanes.c:52
@ STORAGE_MODE_UNSYMMETRIC
Definition: safelanes.c:53
static void safelanes_initOptimizer(void)
Initializes resources used by lane optimization.
Definition: safelanes.c:281
static void safelanes_initStacks_vertex(void)
Sets up the local stacks with entry per vertex (or per jump).
Definition: safelanes.c:350
static cholmod_sparse * QtQ
Definition: safelanes.c:108
static void safelanes_initFTilde(void)
Sets up the fluxes matrix f~.
Definition: safelanes.c:618
SafeLane * safelanes_get(int faction, int standing, const StarSystem *system)
Gets a set of safelanes for a faction and system.
Definition: safelanes.c:187
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.
Definition: safelanes.c:946
static int * sys_to_first_vertex
Definition: safelanes.c:95
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...
Definition: safelanes.c:895
static double * cmp_key_ref
Definition: safelanes.c:112
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.
Definition: safelanes.c:837
static double * tmp_edge_conduct
Definition: safelanes.c:104
int Edge[2]
An edge is a pair of vertex indices.
Definition: safelanes.c:76
static int * tmp_anchor_vertices
Definition: safelanes.c:105
static Vertex * vertex_stack
Definition: safelanes.c:93
static void safelanes_destroyOptimizer(void)
Frees resources used by lane optimization.
Definition: safelanes.c:293
static Edge * tmp_jump_edges
Definition: safelanes.c:103
static int * tmp_spob_indices
Definition: safelanes.c:102
static cholmod_dense * utilde
Definition: safelanes.c:110
VertexType
Object type: like SafeLaneLocType, but with Naev stack indexing in mind.
Definition: safelanes.c:66
static void safelanes_initQtQ(void)
Sets up the (Q*)Q matrix.
Definition: safelanes.c:592
static int FACTION_ID_TO_INDEX(int id)
Return the faction_stack index corresponding to a faction ID, or -1.
Definition: safelanes.c:880
StarSystem * system_getIndex(int id)
Get the system by its index.
Definition: space.c:944
StarSystem * system_getAll(void)
Gets an array (array.h) of all star systems.
Definition: space.c:847
double system_getPresence(const StarSystem *sys, int faction)
Get the presence of a faction in a system.
Definition: space.c:4138
Description of a lane-building faction.
Definition: faction.c:54
double lane_base_cost
Definition: faction.c:90
double lane_length_per_presence
Definition: faction.c:89
int id
Definition: safelanes.c:80
int devmode
Definition: conf.h:158
Describes a safe lane, patrolled by a faction, within a system.
Definition: safelanes.h:24
int point_id[2]
Definition: safelanes.h:27
int faction
Definition: safelanes.h:25
SafeLaneLocType point_type[2]
Definition: safelanes.h:26
double bonus
Definition: space.h:68
double base
Definition: space.h:67
int faction
Definition: space.h:66
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition: space.h:88
SpobPresence presence
Definition: space.h:102
Disjoint set forest on {0, .., n-1}.
Definition: union_find.h:7
Reference to a spob or jump point.
Definition: object.c:56
int index
Definition: safelanes.c:72
int system
Definition: safelanes.c:70
VertexType type
Definition: safelanes.c:71
Represents a 2d vector.
Definition: vec2.h:32
double y
Definition: vec2.h:34
double x
Definition: vec2.h:33
int * unionfind_findall(UnionFind *uf)
Returns a designated representative of each subset in an array (array.h).
Definition: union_find.c:56
int unionfind_find(UnionFind *uf, int x)
Finds the designated representative of the subset containing x.
Definition: union_find.c:48
void unionfind_init(UnionFind *uf, int n)
Creates a UnionFind structure on {0, ..., n}.
Definition: union_find.c:14
void unionfind_union(UnionFind *uf, int x, int y)
Declares x and y to be in the same subset.
Definition: union_find.c:34
void unionfind_free(UnionFind *uf)
Frees resources associated with uf.
Definition: union_find.c:25