Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/python-code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ jobs:
- name: Install Ruff
run: pip install --break-system-packages "ruff==${RUFF_VERSION}"
- name: Run Ruff (output annotations on fixable errors)
run: ruff check --output-format=github . --preview --unsafe-fixes
run: ruff check --output-format=github . --unsafe-fixes
continue-on-error: true
- name: Run Ruff (apply fixes for suggestions)
run: ruff check . --preview --fix --unsafe-fixes
run: ruff check . --fix --unsafe-fixes
- name: Run `ruff format` showing diff without failing
continue-on-error: true
if: ${{ !cancelled() }}
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ repos:
hooks:
# Run the linter.
- id: ruff-check
args: [--fix, --preview]
args: [--fix]
# Run the formatter.
- id: ruff-format
- repo: https://github.com/igorshubovych/markdownlint-cli
Expand Down
2 changes: 2 additions & 0 deletions include/grass/defs/calc.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

extern void calc_init(int);

extern void calc_set_random_state(struct G_random_state *(*)(void));

extern func_t f_add;
extern func_t f_sub;
extern func_t f_mul;
Expand Down
46 changes: 43 additions & 3 deletions lib/calc/xrand.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,48 @@
rand(lo,hi) random values between a and b
****************************************************************/

/* The function set by calc_set_random_state(), or NULL. */
static struct G_random_state *(*random_state)(void);

/*!
* \brief Set the function which gives rand() the random number state
*
* The function must return the state of the calling thread, which the
* tool places for the row being evaluated. Without a function, that is,
* with NULL, rand() draws from the generator shared by the whole program.
*
* \param get_state function returning the calling thread's state, or NULL
*/
void calc_set_random_state(struct G_random_state *(*get_state)(void))
{
random_state = get_state;
}

/* Draw from the state, or from the shared generator without one. */
static unsigned int draw_uint(struct G_random_state *state)
{
if (state)
return G_random_uint32(state);
return (unsigned int)G_mrand48();
}

/* Draw from the state, or from the shared generator without one. */
static double draw_double(struct G_random_state *state)
{
if (state)
return G_random_double(state);
return G_drand48();
}

/*!
* \brief Random value between two values, the upper one excluded
*
* Draws one value per column from the state given by the function set
* with calc_set_random_state(), or from the shared generator.
*/
int f_rand(int argc, const int *argt, void **args)
{
struct G_random_state *state = random_state ? random_state() : NULL;
int i;

if (argc < 2)
Expand All @@ -25,7 +65,7 @@ int f_rand(int argc, const int *argt, void **args)
CELL *arg2 = args[2];

for (i = 0; i < columns; i++) {
unsigned int x = (unsigned int)G_mrand48();
unsigned int x = draw_uint(state);
int lo = arg1[i];
int hi = arg2[i];

Expand All @@ -45,7 +85,7 @@ int f_rand(int argc, const int *argt, void **args)
FCELL *arg2 = args[2];

for (i = 0; i < columns; i++) {
double x = G_drand48();
double x = draw_double(state);
FCELL lo = arg1[i];
FCELL hi = arg2[i];

Expand All @@ -65,7 +105,7 @@ int f_rand(int argc, const int *argt, void **args)
DCELL *arg2 = args[2];

for (i = 0; i < columns; i++) {
double x = G_drand48();
double x = draw_double(state);
DCELL lo = arg1[i];
DCELL hi = arg2[i];

Expand Down
4 changes: 0 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ select = [
"D209", # pydocstyle (D) new-line-after-last-paragraph
"D211", # pydocstyle (D) blank-line-before-class
"D212", # pydocstyle (D) multi-line-summary-first-line
"DOC", # pydoclint (DOC)
"DTZ", # flake8-datetimez (DTZ)
"E", # pycodestyle (E, W)
"EM", # flake8-errmsg (EM)
Expand Down Expand Up @@ -223,9 +222,6 @@ ignore = [
"D415", # missing-terminal-punctuation
"D416", # missing-section-name-colon
"D419", # empty-docstring
"DOC201", # docstring-missing-returns
"DOC402", # docstring-missing-yields
"DOC501", # docstring-missing-exception
"DTZ001", # call-datetime-without-tzinfo
"DTZ002", # call-datetime-today
"DTZ005", # call-datetime-now-without-tzinfo
Expand Down
46 changes: 46 additions & 0 deletions raster/r.mapcalc/evaluate.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <omp.h>
#endif

#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
Expand Down Expand Up @@ -357,6 +358,36 @@ static expr_list *exprs;

/****************************************************************************/

/* The layout rand() draws from, with a row as its unit. Every rand() call
* draws one value per cell, so every row draws columns times calls values.
* A row of r3.mapcalc is numbered across the depths. */
static struct G_random_layout rand_layout;
/* The state of the row each thread evaluates. The states themselves are
* local to the row's evaluation, so the draws of different threads do not
* write to the same cache line. */
static struct G_random_state **rand_state;

/* Give rand() of the calc library the calling thread's state. */
static struct G_random_state *thread_rand_state(void)
{
int tid = 0;
#if defined(_OPENMP)
tid = omp_get_thread_num();
#endif
return rand_state[tid];
}

/* Build the layout for the region and the number of rand() calls. */
static void setup_rand(void)
{
G_random_init_layout_exact(&rand_layout, seed_value, (int64_t)depths * rows,
(int64_t)columns * rand_calls);
if (G_random_layout_batches(&rand_layout) < 1)
G_warning(_("rand() draws more than 2^46 random values. Later "
"rows repeat the values of earlier rows plus a "
"constant."));
}

static void error_handler(void *p G_UNUSED)
{
expr_list *l;
Expand Down Expand Up @@ -421,6 +452,8 @@ void execute(expr_list *ee)
prepare_region_from_maps_intersect(map_list, num_maps);

setup_region();
if (rand_calls > 0)
setup_rand();

/* Parse each expression and initialize the maps, buffers and variables */

Expand Down Expand Up @@ -455,6 +488,10 @@ void execute(expr_list *ee)
}
#endif
current_row = (int *)G_malloc(sizeof(int) * threads);
if (rand_calls > 0) {
rand_state = G_malloc(sizeof(*rand_state) * threads);
calc_set_random_state(thread_rand_state);
}
count = rows * depths;
n = 0;

Expand All @@ -467,11 +504,17 @@ void execute(expr_list *ee)
G_percent(n, count, 2);

int tid = 0;
struct G_random_state row_rand_state;
#if defined(_OPENMP)
tid = omp_get_thread_num();
#endif
/* calculate through expressions row by row */
current_row[tid] = row;
if (rand_calls > 0) {
G_random_state_for_unit(&row_rand_state, &rand_layout,
(int64_t)current_depth * rows + row);
rand_state[tid] = &row_rand_state;
}
for (i = 0; i < num_exprs; i++) {
expression *e = exp_arr[i];
evaluate(e);
Expand Down Expand Up @@ -530,6 +573,9 @@ void execute(expr_list *ee)

/* Free the memory and make it unreachable */
G_free(current_row);
calc_set_random_state(NULL);
G_free(rand_state);
rand_state = NULL;
for (i = 0; i < num_exprs; i++) {
expression *e = exp_arr[i];
free_buf(e);
Expand Down
5 changes: 4 additions & 1 deletion raster/r.mapcalc/globals.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#ifndef __GLOBALS_H_
#define __GLOBALS_H_

#include <stdint.h>

extern int overwrite_flag;
extern long seed_value;
extern int64_t seed_value;
extern long seeded;
extern int rand_calls;
extern int region_approach;

extern int current_depth;
Expand Down
61 changes: 29 additions & 32 deletions raster/r.mapcalc/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
#include <omp.h>
#endif

#include <inttypes.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include <grass/glocale.h>

Expand All @@ -26,8 +26,9 @@

int overwrite_flag;

long seed_value;
int64_t seed_value;
long seeded;
int rand_calls;
int region_approach;

/****************************************************************************/
Expand Down Expand Up @@ -55,40 +56,42 @@ static expr_list *parse_file(const char *filename)
return res;
}

static bool has_rand_expr(const expression *e)
/* Count the rand() calls of an expression. A variable is not followed,
* since the rand() calls of its value are counted where the variable is
* defined, and they draw once per cell however often the variable is
* used. */
static int count_rand_calls(const expression *e)
{
int count = 0;

if (!e)
return 0;

switch (e->type) {
case expr_type_function:
if (strcmp(e->data.func.name, "rand") == 0)
return 1;
count++;
// args is 1-indexed (likely from yacc parser conventions)
for (int i = 1; i <= e->data.func.argc; i++) {
if (has_rand_expr(e->data.func.args[i]))
return 1;
}
return 0;
for (int i = 1; i <= e->data.func.argc; i++)
count += count_rand_calls(e->data.func.args[i]);
return count;

case expr_type_binding:
return has_rand_expr(e->data.bind.val);

case expr_type_variable:
return has_rand_expr(e->data.var.bind);
return count_rand_calls(e->data.bind.val);

default:
return 0;
}
}

static bool expr_list_has_rand(const expr_list *list)
/* Count the rand() calls of all expressions, which every row evaluates. */
static int expr_list_count_rand_calls(const expr_list *list)
{
for (; list; list = list->next) {
if (has_rand_expr(list->exp))
return 1;
}
return 0;
int count = 0;

for (; list; list = list->next)
count += count_rand_calls(list->exp);
return count;
}

int main(int argc, char **argv)
Expand Down Expand Up @@ -187,18 +190,18 @@ int main(int argc, char **argv)
if (!result)
G_fatal_error(_("parse error"));

bool has_rand = expr_list_has_rand(result);
rand_calls = expr_list_count_rand_calls(result);
if (seed->answer) {
seed_value = atol(seed->answer);
G_srand48(seed_value);
seed_value = G_random_seed_from_option(seed);
seeded = 1;
G_debug(3, "Read random seed from seed=: %ld", seed_value);
G_debug(3, "Read random seed from seed=: %" PRId64, seed_value);
}
else {
if (has_rand) {
seed_value = G_srand48_auto();
if (rand_calls > 0) {
seed_value = G_random_generate_seed();
seeded = 1;
G_debug(3, "Automatically generated random seed: %ld", seed_value);
G_debug(3, "Automatically generated random seed: %" PRId64,
seed_value);
}
if (random->answer) {
G_verbose_message(_("Flag 's' is deprecated and will be removed in "
Expand Down Expand Up @@ -239,12 +242,6 @@ int main(int argc, char **argv)
nprocs->answer = "1";
G_verbose_message(_("r3.mapcalc does not support parallel execution."));
}
else if ((threads != 1) && (has_rand)) {
threads = 1;
nprocs->answer = "1";
G_verbose_message(
_("Parallel execution is not supported with rand() function"));
}

/* Ensure the proper number of threads is assigned */
threads = G_set_omp_num_threads(nprocs);
Expand Down
3 changes: 2 additions & 1 deletion raster/r.mapcalc/map.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <grass/config.h>

#include <inttypes.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
Expand Down Expand Up @@ -770,7 +771,7 @@ void create_history(const char *dst, expression *e)
if (seeded) {
char buf[RECORD_LEN];

snprintf(buf, sizeof(buf), "random seed = %ld", seed_value);
snprintf(buf, sizeof(buf), "random seed = %" PRId64, seed_value);
Rast_append_history(&hist, buf);
}

Expand Down
Loading
Loading