Copie figée du cœur de GNU Backgammon (commit 14b6798)

Fichiers repris sans modification dans engine/gnubg/, poids et table
d'équité Kazaross-XG2 dans engine/data/, provenance détaillée dans
engine/gnubg/PROVENANCE.md. Le lint ne vérifie plus les espaces de ces
copies, pour qu'elles restent identiques à l'original.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Pierre MARTIN
2026-11-02 13:56:23 +01:00
co-authored by Claude Opus 5.5
parent d0f44b689b
commit 792f6395bf
43 changed files with 129486 additions and 0 deletions
+349
View File
@@ -0,0 +1,349 @@
/*
* Copyright (C) 1997-2000 Gary Wong <gtw@gnu.org>
* Copyright (C) 2002-2022 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "cache.h"
#include "positionid.h"
#if defined(USE_MULTITHREAD)
#include "multithread.h"
#if defined(__GNUC__) && ( \
(( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) \
&& (defined (__i486) || defined (__x86_64))) \
|| (( __GNUC__ * 100 + __GNUC_MINOR__ >= 404 ) \
&& defined (__i386)) \
)
static inline void
cache_lock(evalCache * pc, uint32_t k)
{
while (__sync_lock_test_and_set(&(pc->entries[k].lock), 1))
while (__sync_fetch_and_add(&pc->entries[k].lock, 0))
__asm__ __volatile__ ("pause":::"memory");
}
static inline void
cache_unlock(evalCache * pc, uint32_t k)
{
__sync_lock_release(&(pc->entries[k].lock));
}
#else /* not x86 or older gcc: no suitable intrinsics */
static inline void
WaitForLock(volatile int *lock)
{
do {
MT_SafeDec(lock);
} while (MT_SafeIncCheck(lock));
}
static inline void
cache_lock(evalCache * pc, uint32_t k)
{
if (MT_SafeIncCheck(&(pc->entries[k].lock)))
WaitForLock(&(pc->entries[k].lock));
}
static inline void
cache_unlock(evalCache * pc, uint32_t k)
{
MT_SafeDec(&(pc->entries[k].lock));
}
#endif
#endif /* USE_MULTITHREAD */
static int
CacheGetSize(unsigned int s)
{
unsigned int size;
if (s == 0)
return 0;
if (s > 1u << 31)
return -1;
/* A cache size of 1 would allocate zero cache nodes */
if (s == 1)
s = 2;
size = s;
/* adjust size to smallest power of 2 GE to s */
while ((s & (s - 1)) != 0)
s &= (s - 1);
size = (s < size) ? 2 * s : s;
/* CacheResize() returns the cache size as an int. */
if (size > (unsigned int) INT_MAX)
return -1;
return (int) size;
}
int
CacheCreate(evalCache * pc, unsigned int s)
{
int size = CacheGetSize(s);
if (size < 0)
return -1;
#if CACHE_STATS
pc->cLookup = 0;
pc->cHit = 0;
pc->nAdds = 0;
#endif
pc->size = (unsigned int) size;
if (pc->size == 0) {
pc->hashMask = 0;
pc->entries = NULL;
return 0;
}
pc->hashMask = (pc->size >> 1) - 1;
/* Avoid "comparison is always false due to limited range of data type"
* warning on 64bits architectures.
* Adapt (or remove the condition) if the involved types are changed.
*/
#if (SIZE_MAX / 64 < UINT_MAX)
/* Ensure the allocation size fits in size_t */
if ((size_t) (pc->size / 2) > SIZE_MAX / sizeof(*pc->entries))
return -1;
#endif
pc->entries = (cacheNode *) malloc((pc->size / 2) * sizeof(*pc->entries));
if (pc->entries == NULL)
return -1;
CacheFlush(pc);
return 0;
}
/* MurmurHash3 https://code.google.com/p/smhasher/wiki/MurmurHash */
extern uint32_t
GetHashKey(uint32_t hashMask, const cacheNodeDetail * restrict e)
{
uint32_t hash = (uint32_t) e->nEvalContext;
int i;
hash *= 0xcc9e2d51;
hash = (hash << 15) | (hash >> (32 - 15));
hash *= 0x1b873593;
hash = (hash << 13) | (hash >> (32 - 13));
hash = hash * 5 + 0xe6546b64;
for (i = 0; i < 7; i++) {
uint32_t k = e->key.data[i];
k *= 0xcc9e2d51;
k = (k << 15) | (k >> (32 - 15));
k *= 0x1b873593;
hash ^= k;
hash = (hash << 13) | (hash >> (32 - 13));
hash = hash * 5 + 0xe6546b64;
}
/* Real MurmurHash3 has a "hash ^= len" here,
* but for us len is constant. Skip it */
hash ^= hash >> 16;
hash *= 0x85ebca6b;
hash ^= hash >> 13;
hash *= 0xc2b2ae35;
hash ^= hash >> 16;
return (hash & hashMask);
}
uint32_t
CacheLookupWithLocking(evalCache * restrict pc, const cacheNodeDetail * restrict e, float * restrict arOut, float * restrict arCubeful)
{
uint32_t const l = GetHashKey(pc->hashMask, e);
#if CACHE_STATS
#if defined(USE_MULTITHREAD)
MT_SafeInc(&pc->cLookup);
#else
++pc->cLookup;
#endif
#endif
#if defined(USE_MULTITHREAD)
cache_lock(pc, l);
#endif
if (!EqualKeys(pc->entries[l].nd_primary.key, e->key) || pc->entries[l].nd_primary.nEvalContext != e->nEvalContext) { /* Not in primary slot */
if (!EqualKeys(pc->entries[l].nd_secondary.key, e->key) || pc->entries[l].nd_secondary.nEvalContext != e->nEvalContext) { /* Cache miss */
#if defined(USE_MULTITHREAD)
cache_unlock(pc, l);
#endif
return l;
} else { /* Found in second slot, promote "hot" entry */
cacheNodeDetail tmp = pc->entries[l].nd_primary;
pc->entries[l].nd_primary = pc->entries[l].nd_secondary;
pc->entries[l].nd_secondary = tmp;
}
}
/* Cache hit */
memcpy(arOut, pc->entries[l].nd_primary.ar, sizeof(float) * 5 /*NUM_OUTPUTS */ );
if (arCubeful)
*arCubeful = pc->entries[l].nd_primary.ar[5]; /* Cubeful equity stored in slot 5 */
#if defined(USE_MULTITHREAD)
cache_unlock(pc, l);
#endif
#if CACHE_STATS
#if defined(USE_MULTITHREAD)
MT_SafeInc(&pc->cHit);
#else
++pc->cHit;
#endif
#endif
return CACHEHIT;
}
uint32_t
CacheLookupNoLocking(evalCache * restrict pc, const cacheNodeDetail * restrict e, float *restrict arOut, float * restrict arCubeful)
{
uint32_t const l = GetHashKey(pc->hashMask, e);
#if CACHE_STATS
++pc->cLookup;
#endif
if (!EqualKeys(pc->entries[l].nd_primary.key, e->key) || pc->entries[l].nd_primary.nEvalContext != e->nEvalContext) { /* Not in primary slot */
if (!EqualKeys(pc->entries[l].nd_secondary.key, e->key) || pc->entries[l].nd_secondary.nEvalContext != e->nEvalContext) { /* Cache miss */
return l;
} else { /* Found in second slot, promote "hot" entry */
cacheNodeDetail tmp = pc->entries[l].nd_primary;
pc->entries[l].nd_primary = pc->entries[l].nd_secondary;
pc->entries[l].nd_secondary = tmp;
}
}
/* Cache hit */
memcpy(arOut, pc->entries[l].nd_primary.ar, sizeof(float) * 5 /*NUM_OUTPUTS */ );
if (arCubeful)
*arCubeful = pc->entries[l].nd_primary.ar[5]; /* Cubeful equity stored in slot 5 */
#if CACHE_STATS
++pc->cHit;
#endif
return CACHEHIT;
}
void
CacheAddWithLocking(evalCache * restrict pc, const cacheNodeDetail * restrict e, uint32_t l)
{
#if defined(USE_MULTITHREAD)
cache_lock(pc, l);
#endif
pc->entries[l].nd_secondary = pc->entries[l].nd_primary;
pc->entries[l].nd_primary = *e;
#if defined(USE_MULTITHREAD)
cache_unlock(pc, l);
#endif
#if CACHE_STATS
#if defined(USE_MULTITHREAD)
MT_SafeInc(&pc->nAdds);
#else
++pc->nAdds;
#endif
#endif
}
/* CacheAddNoLocking() is inlined and in cache.h */
void
CacheDestroy(const evalCache * pc)
{
free(pc->entries);
}
void
CacheFlush(const evalCache * pc)
{
unsigned int k;
for (k = 0; k < pc->size / 2; ++k) {
pc->entries[k].nd_primary.key.data[0] = (unsigned int) -1;
pc->entries[k].nd_secondary.key.data[0] = (unsigned int) -1;
#if defined(USE_MULTITHREAD)
pc->entries[k].lock = 0;
#endif
}
}
int
CacheResize(evalCache * pc, unsigned int cNew)
{
evalCache newCache = { 0 };
int size = CacheGetSize(cNew);
if (size < 0)
return -1;
if ((unsigned int) size == pc->size)
return size;
if (CacheCreate(&newCache, cNew) != 0)
return -1;
CacheDestroy(pc);
*pc = newCache;
return (int) pc->size;
}
#if CACHE_STATS
void
CacheStats(const evalCache * pc, unsigned int *pcLookup, unsigned int *pcHit, unsigned int *pcUsed)
{
if (pcLookup)
*pcLookup = pc->cLookup;
if (pcHit)
*pcHit = pc->cHit;
if (pcUsed)
*pcUsed = pc->nAdds;
}
#endif
+100
View File
@@ -0,0 +1,100 @@
/*
* Copyright (C) 1997-2000 Gary Wong <gtw@gnu.org>
* Copyright (C) 2002-2018 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CACHE_H
#define CACHE_H
#include "config.h"
#ifdef HAVE_STDINT_H
#include <stdint.h>
#else
typedef unsigned int uint32_t;
#endif
#include "gnubg-types.h"
/* Set to calculate simple cache stats */
#define CACHE_STATS 0
typedef struct {
positionkey key; /* size = 28 */
int nEvalContext;
float ar[6];
} cacheNodeDetail;
typedef struct {
cacheNodeDetail nd_primary;
cacheNodeDetail nd_secondary;
#if defined(USE_MULTITHREAD)
int lock;
#endif
} cacheNode;
/* name used in eval.c */
typedef cacheNodeDetail evalcache;
typedef struct {
cacheNode *entries;
unsigned int size;
uint32_t hashMask;
#if CACHE_STATS
unsigned int nAdds;
unsigned int cLookup;
unsigned int cHit;
#endif
} evalCache;
/* Cache size will be adjusted to a power of 2 */
int CacheCreate(evalCache * pc, unsigned int size);
int CacheResize(evalCache * pc, unsigned int cNew);
#define CACHEHIT ((uint32_t)-1)
/* returns a value which is passed to CacheAdd (if a miss) */
uint32_t CacheLookupWithLocking(evalCache * pc, const cacheNodeDetail * e, float *arOut, float *arCubeful);
uint32_t CacheLookupNoLocking(evalCache * pc, const cacheNodeDetail * e, float *arOut, float *arCubeful);
void CacheAddWithLocking(evalCache * pc, const cacheNodeDetail * e, uint32_t l);
static inline void
CacheAddNoLocking(evalCache * pc, const cacheNodeDetail * e, const uint32_t l)
{
pc->entries[l].nd_secondary = pc->entries[l].nd_primary;
pc->entries[l].nd_primary = *e;
#if CACHE_STATS
++pc->nAdds;
#endif
}
void CacheFlush(const evalCache * pc);
void CacheDestroy(const evalCache * pc);
#if CACHE_STATS
void CacheStats(const evalCache * pc, unsigned int *pcLookup, unsigned int *pcHit, unsigned int *pcUsed);
#endif
#if defined(HAVE_FUNC_ATTRIBUTE_PURE)
uint32_t GetHashKey(uint32_t hashMask, const cacheNodeDetail * e) __attribute((pure));
#else
uint32_t GetHashKey(uint32_t hashMask, const cacheNodeDetail * e);
#endif
#endif
+71
View File
@@ -0,0 +1,71 @@
/*
* Copyright (C) 2007-2009 Christian Anthon <anthon@kiku.dk>
* Copyright (C) 2007-2011 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* $Id: gnubg-types.h,v 1.9 2013/06/16 02:16:23 mdpetch Exp $
*/
#ifndef GNUBG_TYPES_H
#define GNUBG_TYPES_H
typedef unsigned int TanBoard[2][25];
typedef const unsigned int (*ConstTanBoard)[25];
typedef enum {
VARIATION_STANDARD, /* standard backgammon */
VARIATION_NACKGAMMON, /* standard backgammon with nackgammon starting
* position */
VARIATION_HYPERGAMMON_1, /* 1-chequer hypergammon */
VARIATION_HYPERGAMMON_2, /* 2-chequer hypergammon */
VARIATION_HYPERGAMMON_3, /* 3-chequer hypergammon */
NUM_VARIATIONS
} bgvariation;
typedef enum {
GAME_NONE, GAME_PLAYING, GAME_OVER, GAME_RESIGNED, GAME_DROP
} gamestate;
typedef struct {
TanBoard anBoard;
unsigned int anDice[2]; /* (0,0) for unrolled dice */
int fTurn; /* who makes the next decision */
int fResigned;
int fResignationDeclined;
int fDoubled;
int cGames;
int fMove; /* player on roll */
int fCubeOwner;
int fCrawford;
int fPostCrawford;
int nMatchTo;
int anScore[2];
int nCube;
unsigned int cBeavers;
bgvariation bgv;
int fCubeUse;
int fJacoby;
gamestate gs;
} matchstate;
typedef union {
unsigned int data[7];
} positionkey;
typedef union {
unsigned char auch[10];
} oldpositionkey;
#endif
+238
View File
@@ -0,0 +1,238 @@
/*
* Copyright (C) 2006 Oystein Johansen <oystein@gnubg.org>
* Copyright (C) 2011-2018 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* $Id: inputs.c,v 1.12 2018/05/12 20:59:35 plm Exp $
*/
#include "config.h"
#include "gnubg-types.h"
#include "simd.h"
#include "eval.h"
#if defined(USE_SIMD_INSTRUCTIONS)
#if defined(USE_NEON)
#include <arm_neon.h>
#elif defined(USE_AVX)
#include <immintrin.h>
#elif defined(USE_SSE2)
#include <emmintrin.h>
#else
#include <xmmintrin.h>
#endif
#else
typedef float float_vector[4];
#endif /* USE_SIMD_INSTRUCTIONS */
typedef SSE_ALIGN(float float_vec_aligned[sizeof(float_vector)/sizeof(float)]);
SSE_ALIGN (static float_vec_aligned inpvec[16]) = {
/* 0 */ { 0.0, 0.0, 0.0, 0.0},
/* 1 */ { 1.0, 0.0, 0.0, 0.0},
/* 2 */ { 0.0, 1.0, 0.0, 0.0},
/* 3 */ { 0.0, 0.0, 1.0, 0.0},
/* 4 */ { 0.0, 0.0, 1.0, 0.5},
/* 5 */ { 0.0, 0.0, 1.0, 1.0},
/* 6 */ { 0.0, 0.0, 1.0, 1.5},
/* 7 */ { 0.0, 0.0, 1.0, 2.0},
/* 8 */ { 0.0, 0.0, 1.0, 2.5},
/* 9 */ { 0.0, 0.0, 1.0, 3.0},
/* 10 */ { 0.0, 0.0, 1.0, 3.5},
/* 11 */ { 0.0, 0.0, 1.0, 4.0},
/* 12 */ { 0.0, 0.0, 1.0, 4.5},
/* 13 */ { 0.0, 0.0, 1.0, 5.0},
/* 14 */ { 0.0, 0.0, 1.0, 5.5},
/* 15 */ { 0.0, 0.0, 1.0, 6.0}};
SSE_ALIGN(static float_vec_aligned inpvecb[16]) = {
/* 0 */ { 0.0, 0.0, 0.0, 0.0},
/* 1 */ { 1.0, 0.0, 0.0, 0.0},
/* 2 */ { 1.0, 1.0, 0.0, 0.0},
/* 3 */ { 1.0, 1.0, 1.0, 0.0},
/* 4 */ { 1.0, 1.0, 1.0, 0.5},
/* 5 */ { 1.0, 1.0, 1.0, 1.0},
/* 6 */ { 1.0, 1.0, 1.0, 1.5},
/* 7 */ { 1.0, 1.0, 1.0, 2.0},
/* 8 */ { 1.0, 1.0, 1.0, 2.5},
/* 9 */ { 1.0, 1.0, 1.0, 3.0},
/* 10 */ { 1.0, 1.0, 1.0, 3.5},
/* 11 */ { 1.0, 1.0, 1.0, 4.0},
/* 12 */ { 1.0, 1.0, 1.0, 4.5},
/* 13 */ { 1.0, 1.0, 1.0, 5.0},
/* 14 */ { 1.0, 1.0, 1.0, 5.5},
/* 15 */ { 1.0, 1.0, 1.0, 6.0}};
#if defined(USE_SIMD_INSTRUCTIONS)
extern SIMD_AVX_STACKALIGN void
baseInputs(const TanBoard anBoard, float arInput[])
{
int i = 3;
const unsigned int *pB = &anBoard[0][0];
float *pInput = &arInput[0];
#if defined(HAVE_SSE)
__m128 vec0;
__m128 vec1;
__m128 vec2;
__m128 vec3;
__m128 vec4;
__m128 vec5;
__m128 vec6;
__m128 vec7;
#elif defined(HAVE_NEON)
float32x4_t vec0;
float32x4_t vec1;
float32x4_t vec2;
float32x4_t vec3;
float32x4_t vec4;
float32x4_t vec5;
float32x4_t vec6;
float32x4_t vec7;
#endif
while (i--) {
#if defined(HAVE_SSE)
vec0 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput, vec0);
vec1 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec1);
vec2 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec2);
vec3 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec3);
vec4 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec4);
vec5 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec5);
vec6 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec6);
vec7 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec7);
#else
vec0 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput, vec0);
vec1 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec1);
vec2 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec2);
vec3 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec3);
vec4 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec4);
vec5 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec5);
vec6 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec6);
vec7 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec7);
#endif
pInput += 4;
}
/* bar */
#if defined(HAVE_SSE)
vec0 = _mm_load_ps(inpvecb[*pB++]);
_mm_store_ps(pInput, vec0);
#else
vec0 = vld1q_f32(inpvecb[*pB++]);
vst1q_f32(pInput, vec0);
#endif
pInput += 4;
i = 3;
while (i--) {
#if defined(HAVE_SSE)
vec0 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput, vec0);
vec1 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec1);
vec2 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec2);
vec3 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec3);
vec4 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec4);
vec5 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec5);
vec6 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec6);
vec7 = _mm_load_ps(inpvec[*pB++]);
_mm_store_ps(pInput += 4, vec7);
#else
vec0 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput, vec0);
vec1 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec1);
vec2 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec2);
vec3 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec3);
vec4 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec4);
vec5 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec5);
vec6 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec6);
vec7 = vld1q_f32(inpvec[*pB++]);
vst1q_f32(pInput += 4, vec7);
#endif
pInput += 4;
}
/* bar */
#if defined(HAVE_SSE)
vec0 = _mm_load_ps(inpvecb[*pB]);
_mm_store_ps(pInput, vec0);
#else
vec0 = vld1q_f32(inpvecb[*pB++]);
vst1q_f32(pInput, vec0);
#endif
return;
}
#else
extern void
baseInputs(const TanBoard anBoard, float arInput[])
{
int j, i;
for (j = 0; j < 2; ++j) {
float *afInput = arInput + j * 25 * 4;
const unsigned int *board = anBoard[j];
/* Points */
for (i = 0; i < 24; i++) {
const unsigned int nc = board[i];
afInput[i * 4 + 0] = inpvec[nc][0];
afInput[i * 4 + 1] = inpvec[nc][1];
afInput[i * 4 + 2] = inpvec[nc][2];
afInput[i * 4 + 3] = inpvec[nc][3];
}
/* Bar */
{
const unsigned int nc = board[24];
afInput[24 * 4 + 0] = inpvecb[nc][0];
afInput[24 * 4 + 1] = inpvecb[nc][1];
afInput[24 * 4 + 2] = inpvecb[nc][2];
afInput[24 * 4 + 3] = inpvecb[nc][3];
}
}
}
#endif
+145
View File
@@ -0,0 +1,145 @@
/*
* -----------------------------------------------------------------------------
* rand.c: By Bob Jenkins. My random number generator, ISAAC. Public Domain.
* MODIFIED:
* 960327: Creation (addition of randinit, really)
* 970719: use context, not global variables, for internal state
* 980324: added main (ifdef'ed out), also rearranged randinit()
* 010626: Note that this is public domain
* -----------------------------------------------------------------------------
*/
/*
* Minor modifications for use with GNU Backgammon.
* Copyright (C) 1999-2007 the AUTHORS
*
* $Id: isaac.c,v 1.8 2013/06/16 02:16:24 mdpetch Exp $
*/
/*lint --e{661,662} Ignore warnings of out of bounds array access (as ok) */
/* No configuration used in this file
* #include "config.h" */
#include "isaac.h"
#define ind(mm,x) ((mm)[((x)>>2)&(RANDSIZ-1)])
#define rngstep(mix,a,b,mm,m,m2,r,x) \
{ \
x = *m; \
a = ((a^(mix)) + *(m2++)) & 0xffffffff; \
*(m++) = y = (ind(mm,x) + a + b) & 0xffffffff; \
*(r++) = b = (ind(mm,y>>RANDSIZL) + x) & 0xffffffff; \
}
void
isaac(randctx * ctx)
{
register ub4 a, b, x, y, *m, *mm, *m2, *r, *mend;
mm = ctx->randmem;
r = ctx->randrsl;
a = ctx->randa;
b = (ctx->randb + (++ctx->randc)) & 0xffffffff;
for (m = mm, mend = m2 = m + (RANDSIZ / 2); m < mend;) {
rngstep(a << 13, a, b, mm, m, m2, r, x);
rngstep(a >> 6, a, b, mm, m, m2, r, x);
rngstep(a << 2, a, b, mm, m, m2, r, x);
rngstep(a >> 16, a, b, mm, m, m2, r, x);
}
for (m2 = mm; m2 < mend;) {
rngstep(a << 13, a, b, mm, m, m2, r, x);
rngstep(a >> 6, a, b, mm, m, m2, r, x);
rngstep(a << 2, a, b, mm, m, m2, r, x);
rngstep(a >> 16, a, b, mm, m, m2, r, x);
}
ctx->randb = b;
ctx->randa = a;
}
#define mix(a,b,c,d,e,f,g,h) \
{ \
a^=b<<11; d+=a; b+=c; \
b^=c>>2; e+=b; c+=d; \
c^=d<<8; f+=c; d+=e; \
d^=e>>16; g+=d; e+=f; \
e^=f<<10; h+=e; f+=g; \
f^=g>>4; a+=f; g+=h; \
g^=h<<8; b+=g; h+=a; \
h^=a>>9; c+=h; a+=b; \
}
/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */
void
irandinit(randctx * ctx, word flag)
{
word i;
ub4 a, b, c, d, e, f, g, h;
ub4 *m, *r;
ctx->randa = ctx->randb = ctx->randc = 0;
m = ctx->randmem;
r = ctx->randrsl;
a = b = c = d = e = f = g = h = 0x9e3779b9; /* the golden ratio */
for (i = 0; i < 4; ++i) { /* scramble it */
mix(a, b, c, d, e, f, g, h);
}
if (flag) {
/* initialize using the contents of r[] as the seed */
for (i = 0; i < RANDSIZ; i += 8) {
a += r[i];
b += r[i + 1];
c += r[i + 2];
d += r[i + 3];
e += r[i + 4];
f += r[i + 5];
g += r[i + 6];
h += r[i + 7];
mix(a, b, c, d, e, f, g, h);
m[i] = a;
m[i + 1] = b;
m[i + 2] = c;
m[i + 3] = d;
m[i + 4] = e;
m[i + 5] = f;
m[i + 6] = g;
m[i + 7] = h;
}
/* do a second pass to make all of the seed affect all of m */
for (i = 0; i < RANDSIZ; i += 8) {
a += m[i];
b += m[i + 1];
c += m[i + 2];
d += m[i + 3];
e += m[i + 4];
f += m[i + 5];
g += m[i + 6];
h += m[i + 7];
mix(a, b, c, d, e, f, g, h);
m[i] = a;
m[i + 1] = b;
m[i + 2] = c;
m[i + 3] = d;
m[i + 4] = e;
m[i + 5] = f;
m[i + 6] = g;
m[i + 7] = h;
}
} else {
for (i = 0; i < RANDSIZ; i += 8) {
/* fill in mm[] with messy stuff */
mix(a, b, c, d, e, f, g, h);
m[i] = a;
m[i + 1] = b;
m[i + 2] = c;
m[i + 3] = d;
m[i + 4] = e;
m[i + 5] = f;
m[i + 6] = g;
m[i + 7] = h;
}
}
isaac(ctx); /* fill in the first set of results */
ctx->randcnt = RANDSIZ; /* prepare to use the first set of results */
}
+60
View File
@@ -0,0 +1,60 @@
/*
* -----------------------------------------------------------------------------
* rand.h: definitions for a random number generator
* By Bob Jenkins, 1996, Public Domain
* MODIFIED:
* 960327: Creation (addition of randinit, really)
* 970719: use context, not global variables, for internal state
* 980324: renamed seed to flag
* 980605: recommend RANDSIZL=4 for noncryptography.
* 010626: note this is public domain
* -----------------------------------------------------------------------------
*/
/*
* Minor modifications for use with GNU Backgammon.
* Copyright (C) 1999-2006 the AUTHORS
*
* $Id: isaac.h,v 1.5 2013/06/16 02:16:24 mdpetch Exp $
*/
#include "isaacs.h"
#ifndef ISAAC_H
#define ISAAC_H
#define RANDSIZL (4) /* I recommend 8 for crypto, 4 for simulations */
#define RANDSIZ (1<<RANDSIZL)
/* context of random number generator */
struct randctx {
ub4 randcnt;
ub4 randrsl[RANDSIZ];
ub4 randmem[RANDSIZ];
ub4 randa;
ub4 randb;
ub4 randc;
};
typedef struct randctx randctx;
/*
* ------------------------------------------------------------------------------
* If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed.
* ------------------------------------------------------------------------------
*/
void irandinit(randctx * r, word flag);
void isaac(randctx * r);
/*
* ------------------------------------------------------------------------------
* Call irand(/o_ randctx *r _o/) to retrieve a single 32-bit random value
* ------------------------------------------------------------------------------
*/
#define irand(r) \
(!(r)->randcnt-- ? \
(isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \
(r)->randrsl[(r)->randcnt])
#endif
+47
View File
@@ -0,0 +1,47 @@
/*
* -----------------------------------------------------------------------------
* standard.hi: Standard definitions and types, Bob Jenkins
* -----------------------------------------------------------------------------
*/
/*
* Minor modifications for use with GNU Backgammon.
* Copyright (C) 1999-2013 the AUTHORS
*
* $Id: isaacs.h,v 1.5 2013/09/09 21:06:21 plm Exp $
*/
#ifndef ISAACS_H
#define ISAACS_H
#if 0
/* Pre-64bits relics. Most of them are not used anyway */
typedef unsigned long int ub4; /* unsigned 4-byte quantities */
#define UB4MAXVAL 0xffffffff
typedef signed long int sb4;
#define SB4MAXVAL 0x7fffffff
typedef unsigned short int ub2;
#define UB2MAXVAL 0xffff
typedef signed short int sb2;
#define SB2MAXVAL 0x7fff
typedef unsigned char ub1;
#define UB1MAXVAL 0xff
typedef signed char sb1; /* signed 1-byte quantities */
#define SB1MAXVAL 0x7f
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#else
typedef unsigned int uint32_t;
#endif
typedef uint32_t ub4; /* unsigned 4-byte quantities */
#define UB4MAXVAL 0xffffffff
typedef int word; /* fastest type available */
#endif
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright (C) 1996-2001 Gary Wong <gtw@gnu.org>
* Copyright (C) 2004-2026 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* No configuration used in this file
* #include "config.h" */
#include "list.h"
#include <stddef.h>
#include <stdlib.h>
#include <glib.h>
int
ListCreate(listOLD * pl)
{
pl->plPrev = pl->plNext = pl;
pl->p = NULL;
return 0;
}
listOLD *
ListInsert(listOLD * pl, void *p)
{
listOLD *plNew = g_malloc(sizeof(listOLD));
plNew->p = p;
plNew->plNext = pl;
plNew->plPrev = pl->plPrev;
pl->plPrev = plNew;
plNew->plPrev->plNext = plNew;
return plNew;
}
void
ListDelete(listOLD * pl)
{
pl->plPrev->plNext = pl->plNext;
pl->plNext->plPrev = pl->plPrev;
g_free(pl);
}
void
ListDeleteAll(const listOLD * pl)
{
listOLD *pc = pl->plNext;
while (pc != pl) {
listOLD *pn = pc->plNext;
g_free(pc->p);
ListDelete(pc);
pc = pn;
}
g_assert(ListEmpty(pl));
}
+39
View File
@@ -0,0 +1,39 @@
/*
* Copyright (C) 1996-1999 Gary Wong <gtw@gnu.org>
* Copyright (C) 2004-2026 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef LIST_H
#define LIST_H
typedef struct list {
struct list *plPrev;
struct list *plNext;
void *p;
} listOLD;
/* Renamed to listOLD - use GList instead (hopefullly replace existing usage eventually */
extern int ListCreate(listOLD * pl);
extern listOLD *ListInsert(listOLD * pl, void *p);
extern void ListDelete(listOLD * pl);
extern void ListDeleteAll(const listOLD * pl);
static inline int ListEmpty(const listOLD *pl)
{
return ((pl->plNext == pl) && (pl->plPrev == pl));
}
#endif
+407
View File
@@ -0,0 +1,407 @@
/*
* Functions to compute MD5 message digest of files or memory blocks.
* according to the definition of MD5 in RFC 1321 from April 1992,
* by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
* Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
*
* Minor modifications for use with GNU Backgammon.
* Copyright (C) 2000-2019 the AUTHORS
*
* License changed from the GNU LGPL to the GNU GPL (as permitted under Term 3
* of the GNU LGPL) by Gary Wong for distribution with GNU Backgammon.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* $Id: md5.c,v 1.15 2019/12/19 09:23:04 Superfly_Jon Exp $
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "md5.h"
/* Be conservative and always perform explicit byte-ordering. This is
* redundant on little-endian architectures, but is portable. */
#define WRITE( p, n ) \
( ( (unsigned char *) (p) )[ 0 ] = (unsigned char)( (n) & 0xFF ), \
( (unsigned char *) (p) )[ 1 ] = (unsigned char)(( (n) & 0xFF00 ) >> 8), \
( (unsigned char *) (p) )[ 2 ] = (unsigned char)(( (n) & 0xFF0000 ) >> 16), \
( (unsigned char *) (p) )[ 3 ] = (unsigned char)(( (n) & 0xFF000000 ) >> 24) )
#define READ( p ) \
( ( (const unsigned char *) (p) )[ 0 ] | \
( (unsigned int)((const unsigned char *) (p) )[ 1 ]) << 8 | \
( (unsigned int)((const unsigned char *) (p) )[ 2 ]) << 16 | \
( (unsigned int)((const unsigned char *) (p) )[ 3 ]) << 24 )
/* This array contains the bytes used to pad the buffer to the next
* 64-byte boundary. (RFC 1321, 3.1: Step 1) */
/* But this isn't used ???? */
/*lint -e{785} Ignore not enough initilizer warning */
static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
/* Initialize structure containing state of computation.
* (RFC 1321, 3.3: Step 3) */
void
md5_init_ctx(struct md5_ctx *ctx)
{
ctx->A = 0x67452301;
ctx->B = 0xefcdab89;
ctx->C = 0x98badcfe;
ctx->D = 0x10325476;
ctx->total[0] = ctx->total[1] = 0;
ctx->buflen = 0;
}
/* Put result from CTX in first 16 bytes following RESBUF. The result
* must be in little endian byte order.
*
* IMPORTANT: On some systems it is required that RESBUF is correctly
* aligned for a 32 bits value. */
void *
md5_read_ctx(const struct md5_ctx *ctx, void *resbuf)
{
char *r = (char *) resbuf;
WRITE(r, ctx->A);
WRITE(r + 4, ctx->B);
WRITE(r + 8, ctx->C);
WRITE(r + 12, ctx->D);
return resbuf;
}
/* Process the remaining bytes in the internal buffer and the usual
* prolog according to the standard and write the result to RESBUF.
*
* IMPORTANT: On some systems it is required that RESBUF is correctly
* aligned for a 32 bits value. */
void *
md5_finish_ctx(struct md5_ctx *ctx, void *resbuf)
{
/* Take yet unprocessed bytes into account. */
md5_uint32 bytes = ctx->buflen;
size_t pad;
/* Now count remaining bytes. */
ctx->total[0] += bytes;
if (ctx->total[0] < bytes)
++ctx->total[1];
pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
memcpy(&ctx->buffer[bytes], fillbuf, pad);
/* Put the 64-bit file length in *bits* at the end of the buffer. */
WRITE(ctx->buffer + bytes + pad, ctx->total[0] << 3);
WRITE(ctx->buffer + bytes + pad + 4, ((ctx->total[1] << 3) | (ctx->total[0] >> 29)));
/* Process last bytes. */
md5_process_block(ctx->buffer, bytes + pad + 8, ctx);
return md5_read_ctx(ctx, resbuf);
}
#if 0
/* Compute MD5 message digest for bytes read from STREAM. The
* resulting message digest number will be written into the 16 bytes
* beginning at RESBLOCK. */
int
md5_stream(FILE * stream, void *resblock)
{
/* Important: BLOCKSIZE must be a multiple of 64. */
#define BLOCKSIZE 4096
struct md5_ctx ctx;
char buffer[BLOCKSIZE + 72];
size_t sum;
/* Initialize the computation context. */
md5_init_ctx(&ctx);
/* Iterate over full file contents. */
for (;;) {
/* We read the file in blocks of BLOCKSIZE bytes. One call of the
* computation function processes the whole buffer so that with the
* next round of the loop another block can be read. */
size_t n;
sum = 0;
/* Read block. Take care for partial reads. */
do {
n = fread(buffer + sum, 1, BLOCKSIZE - sum, stream);
sum += n;
}
while (sum < BLOCKSIZE && n != 0);
if (n == 0 && ferror(stream))
return 1;
/* If end of file is reached, end the loop. */
if (n == 0)
break;
/* Process buffer with BLOCKSIZE bytes. Note that
* BLOCKSIZE % 64 == 0
*/
md5_process_block(buffer, BLOCKSIZE, &ctx);
}
/* Add the last bytes if necessary. */
if (sum > 0)
md5_process_bytes(buffer, sum, &ctx);
/* Construct result in desired memory. */
md5_finish_ctx(&ctx, resblock);
return 0;
}
#endif
/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
* result is always in little endian byte order, so that a byte-wise
* output yields to the wanted ASCII representation of the message
* digest. */
void *
md5_buffer(const char *buffer, size_t len, void *resblock)
{
struct md5_ctx ctx;
/* Initialize the computation context. */
md5_init_ctx(&ctx);
/* Process whole buffer but last len % 64 bytes. */
md5_process_bytes(buffer, len, &ctx);
/* Put result in desired memory area. */
return md5_finish_ctx(&ctx, resblock);
}
void
md5_process_bytes(const void *buffer, size_t len, struct md5_ctx *ctx)
{
/* const void aligned_buffer = buffer; */
/* When we already have some bits in our internal buffer concatenate
* both inputs first. */
if (ctx->buflen != 0) {
size_t left_over = ctx->buflen;
size_t add = 128 - left_over > len ? len : 128 - left_over;
/* Only put full words in the buffer. */
add -= add % sizeof(md5_uint32);
memcpy(&ctx->buffer[left_over], buffer, add);
ctx->buflen += (md5_uint32) add;
if (ctx->buflen > 64) {
md5_process_block(ctx->buffer, ctx->buflen & ~63U, ctx);
ctx->buflen &= 63;
/* The regions in the following copy operation cannot overlap. */
memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63U], ctx->buflen);
}
buffer = (const char *) buffer + add;
len -= add;
}
/* Process available complete blocks. */
if (len > 64) {
md5_process_block(buffer, len & ~63U, ctx);
buffer = (const char *) buffer + (len & ~63U);
len &= 63;
}
/* Move remaining bytes in internal buffer. */
if (len > 0) {
size_t left_over = ctx->buflen;
memcpy(&ctx->buffer[left_over], buffer, len);
left_over += len;
if (left_over >= 64) {
md5_process_block(ctx->buffer, 64, ctx);
left_over -= 64;
memcpy(ctx->buffer, &ctx->buffer[64], left_over);
}
ctx->buflen = (md5_uint32) left_over;
}
}
/* These are the four functions used in the four steps of the MD5 algorithm
* and defined in the RFC 1321. The first function is a little bit optimized
* (as found in Colin Plumbs public domain implementation). */
/* #define FF(b, c, d) ((b & c) | (~b & d)) */
#define FF(b, c, d) (d ^ (b & (c ^ d)))
#define FG(b, c, d) FF (d, b, c)
#define FH(b, c, d) (b ^ c ^ d)
#define FI(b, c, d) (c ^ (b | ~d))
/* Process LEN bytes of BUFFER, accumulating context into CTX.
* It is assumed that LEN % 64 == 0. */
void
md5_process_block(const void *buffer, size_t len, struct md5_ctx *ctx)
{
md5_uint32 correct_words[16];
const md5_uint32 *words = (const md5_uint32 *) buffer;
size_t nwords = len / sizeof(md5_uint32);
const md5_uint32 *endp = words + nwords;
md5_uint32 A = ctx->A;
md5_uint32 B = ctx->B;
md5_uint32 C = ctx->C;
md5_uint32 D = ctx->D;
/* First increment the byte count. RFC 1321 specifies the possible
* length of the file up to 2^64 bits. Here we only compute the
* number of bytes. Do a double word increment. */
ctx->total[0] += (md5_uint32) len;
if (ctx->total[0] < len)
++ctx->total[1];
/* Process all bytes in the buffer with 64 bytes in each round of
* the loop. */
while (words < endp) {
md5_uint32 *cwp = correct_words;
md5_uint32 A_save = A;
md5_uint32 B_save = B;
md5_uint32 C_save = C;
md5_uint32 D_save = D;
/* First round: using the given function, the context and a constant
* the next context is computed. Because the algorithms processing
* unit is a 32-bit word and it is determined to work on words in
* little endian byte order we perhaps have to change the byte order
* before the computation. To reduce the work for the next steps
* we store the swapped words in the array CORRECT_WORDS. */
#define OP(a, b, c, d, s, T) \
do \
{ \
a += FF (b, c, d) + (*cwp++ = READ (words)) + T; \
++words; \
CYCLIC (a, s); \
a += b; \
} \
while (0)
/* It is unfortunate that C does not provide an operator for
* cyclic rotation. Hope the C compiler is smart enough. */
#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
/* Before we start, one word to the strange constants.
* They are defined in RFC 1321 as
*
* T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
*/
/* Round 1. */
/*lint --e{123, 717} Ignore macro warning */
OP(A, B, C, D, 7, 0xd76aa478);
OP(D, A, B, C, 12, 0xe8c7b756);
OP(C, D, A, B, 17, 0x242070db);
OP(B, C, D, A, 22, 0xc1bdceee);
OP(A, B, C, D, 7, 0xf57c0faf);
OP(D, A, B, C, 12, 0x4787c62a);
OP(C, D, A, B, 17, 0xa8304613);
OP(B, C, D, A, 22, 0xfd469501);
OP(A, B, C, D, 7, 0x698098d8);
OP(D, A, B, C, 12, 0x8b44f7af);
OP(C, D, A, B, 17, 0xffff5bb1);
OP(B, C, D, A, 22, 0x895cd7be);
OP(A, B, C, D, 7, 0x6b901122);
OP(D, A, B, C, 12, 0xfd987193);
OP(C, D, A, B, 17, 0xa679438e);
OP(B, C, D, A, 22, 0x49b40821);
/* For the second to fourth round we have the possibly swapped words
* in CORRECT_WORDS. Redefine the macro to take an additional first
* argument specifying the function to use. */
#undef OP
#define OP(f, a, b, c, d, k, s, T) \
do \
{ \
a += f (b, c, d) + correct_words[k] + T; \
CYCLIC (a, s); \
a += b; \
} \
while (0)
/* Round 2. */
OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
OP(FG, D, A, B, C, 6, 9, 0xc040b340);
OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
OP(FG, D, A, B, C, 10, 9, 0x02441453);
OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
/* Round 3. */
OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
OP(FH, D, A, B, C, 8, 11, 0x8771f681);
OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
OP(FH, B, C, D, A, 6, 23, 0x04881d05);
OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
/* Round 4. */
OP(FI, A, B, C, D, 0, 6, 0xf4292244);
OP(FI, D, A, B, C, 7, 10, 0x432aff97);
OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
OP(FI, C, D, A, B, 6, 15, 0xa3014314);
OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
/* Add the starting values of the context. */
A += A_save;
B += B_save;
C += C_save;
D += D_save;
}
/* Put checksum in context given as argument. */
ctx->A = A;
ctx->B = B;
ctx->C = C;
ctx->D = D;
}
+101
View File
@@ -0,0 +1,101 @@
/*
* Declaration of functions and data types used for MD5 sum computing
* library functions.
* Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
*
* Minor modifications for use with GNU Backgammon.
* Copyright (C) 2009-2021 the AUTHORS
*
* License changed from the GNU LGPL to the GNU GPL (as permitted under Term 3
* of the GNU LGPL) by Gary Wong for distribution with GNU Backgammon.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* $Id: md5.h,v 1.8 2014/05/11 15:20:32 plm Exp $
*/
#ifndef MD5_H
#define MD5_H 1
#include <stdint.h>
#include <stdio.h>
#include "common.h"
typedef uint32_t md5_uint32;
/* Structure to save state of computation between the single steps. */
struct md5_ctx {
md5_uint32 A;
md5_uint32 B;
md5_uint32 C;
md5_uint32 D;
md5_uint32 total[2];
md5_uint32 buflen;
char buffer[128] __attribute__ ((__aligned__(__alignof__(md5_uint32))));
};
/*
* The following three functions are build up the low level used in
* the functions `md5_stream' and `md5_buffer'.
*/
/* Initialize structure containing state of computation.
* (RFC 1321, 3.3: Step 3) */
extern void md5_init_ctx(struct md5_ctx *ctx);
/* Starting with the result of former calls of this function (or the
* initialization function) update the context for the next LEN bytes
* starting at BUFFER.
* It is necessary that LEN is a multiple of 64!!! */
extern void md5_process_block(const void *buffer, size_t len, struct md5_ctx *ctx);
/* Starting with the result of former calls of this function (or the
* initialization function) update the context for the next LEN bytes
* starting at BUFFER.
* It is NOT required that LEN is a multiple of 64. */
extern void md5_process_bytes(const void *buffer, size_t len, struct md5_ctx *ctx);
/* Process the remaining bytes in the buffer and put result from CTX
* in first 16 bytes following RESBUF. The result is always in little
* endian byte order, so that a byte-wise output yields to the wanted
* ASCII representation of the message digest.
*
* IMPORTANT: On some systems it is required that RESBUF is correctly
* aligned for a 32 bits value. */
extern void *md5_finish_ctx(struct md5_ctx *ctx, void *resbuf);
/* Put result from CTX in first 16 bytes following RESBUF. The result is
* always in little endian byte order, so that a byte-wise output yields
* to the wanted ASCII representation of the message digest.
*
* IMPORTANT: On some systems it is required that RESBUF is correctly
* aligned for a 32 bits value. */
extern void *md5_read_ctx(const struct md5_ctx *ctx, void *resbuf);
#if 0
/* Compute MD5 message digest for bytes read from STREAM. The
* resulting message digest number will be written into the 16 bytes
* beginning at RESBLOCK. */
extern int md5_stream(FILE * stream, void *resblock);
#endif
/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
* result is always in little endian byte order, so that a byte-wise
* output yields to the wanted ASCII representation of the message
* digest. */
extern void *md5_buffer(const char *buffer, size_t len, void *resblock);
#endif /* md5.h */
+569
View File
@@ -0,0 +1,569 @@
/*
* Copyright (C) 1998-2002 Gary Wong <gtw@gnu.org>
* Copyright (C) 2000-2019 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "backgammon.h"
#include "common.h"
#include <glib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include "neuralnet.h"
#include "simd.h"
#include "sigmoid.h"
static int
NeuralNetCreate(neuralnet * pnn, unsigned int cInput, unsigned int cHidden,
unsigned int cOutput, float rBetaHidden, float rBetaOutput)
{
pnn->cInput = cInput;
pnn->cHidden = cHidden;
pnn->cOutput = cOutput;
pnn->rBetaHidden = rBetaHidden;
pnn->rBetaOutput = rBetaOutput;
pnn->nTrained = 0;
if ((pnn->arHiddenWeight = sse_malloc(cHidden * cInput * sizeof(float))) == NULL)
return -1;
if ((pnn->arOutputWeight = sse_malloc(cOutput * cHidden * sizeof(float))) == NULL) {
sse_free(pnn->arHiddenWeight);
return -1;
}
if ((pnn->arHiddenThreshold = sse_malloc(cHidden * sizeof(float))) == NULL) {
sse_free(pnn->arOutputWeight);
sse_free(pnn->arHiddenWeight);
return -1;
}
if ((pnn->arOutputThreshold = sse_malloc(cOutput * sizeof(float))) == NULL) {
sse_free(pnn->arHiddenThreshold);
sse_free(pnn->arOutputWeight);
sse_free(pnn->arHiddenWeight);
return -1;
}
return 0;
}
extern void
NeuralNetDestroy(neuralnet * pnn)
{
sse_free(pnn->arHiddenWeight);
pnn->arHiddenWeight = 0;
sse_free(pnn->arOutputWeight);
pnn->arOutputWeight = 0;
sse_free(pnn->arHiddenThreshold);
pnn->arHiddenThreshold = 0;
sse_free(pnn->arOutputThreshold);
pnn->arOutputThreshold = 0;
}
#if !defined(USE_SIMD_INSTRUCTIONS)
/* separate context for race, crashed, contact
* -1: regular eval
* 0: save base
* 1: from base
*/
static inline NNEvalType
NNevalAction(NNState * pnState)
{
if (!pnState)
return NNEVAL_NONE;
switch (pnState->state) {
case NNSTATE_NONE:
{
/* incremental evaluation not useful */
return NNEVAL_NONE;
}
case NNSTATE_INCREMENTAL:
{
/* next call should return FROMBASE */
pnState->state = NNSTATE_DONE;
/* starting a new context; save base in the hope it will be useful */
return NNEVAL_SAVE;
}
case NNSTATE_DONE:
{
/* context hit! use the previously computed base */
return NNEVAL_FROMBASE;
}
}
/* never reached */
return NNEVAL_NONE; /* for the picky compiler */
}
static void
Evaluate(const neuralnet * pnn, const float arInput[], float ar[], float arOutput[], float *saveAr)
{
const unsigned int cHidden = pnn->cHidden;
unsigned int i, j;
float *prWeight;
/* Calculate activity at hidden nodes */
for (i = 0; i < cHidden; i++)
ar[i] = pnn->arHiddenThreshold[i];
prWeight = pnn->arHiddenWeight;
for (i = 0; i < pnn->cInput; i++) {
float const ari = arInput[i];
if (ari == 0.0f)
prWeight += cHidden;
else {
float *pr = ar;
if (ari == 1.0f)
for (j = cHidden; j; j--)
*pr++ += *prWeight++;
else
for (j = cHidden; j; j--)
*pr++ += *prWeight++ * ari;
}
}
if (saveAr)
memcpy(saveAr, ar, cHidden * sizeof(*saveAr));
for (i = 0; i < cHidden; i++)
ar[i] = sigmoid(-pnn->rBetaHidden * ar[i]);
/* Calculate activity at output nodes */
prWeight = pnn->arOutputWeight;
for (i = 0; i < pnn->cOutput; i++) {
float r = pnn->arOutputThreshold[i];
for (j = 0; j < cHidden; j++)
r += ar[j] * *prWeight++;
arOutput[i] = sigmoid(-pnn->rBetaOutput * r);
}
}
static void
EvaluateFromBase(const neuralnet * pnn, const float arInputDif[], float ar[], float arOutput[])
{
unsigned int i, j;
float *prWeight;
/* Calculate activity at hidden nodes */
/* for( i = 0; i < pnn->cHidden; i++ )
* ar[ i ] = pnn->arHiddenThreshold[ i ]; */
prWeight = pnn->arHiddenWeight;
for (i = 0; i < pnn->cInput; ++i) {
float const ari = arInputDif[i];
if (ari == 0.0f)
prWeight += pnn->cHidden;
else {
float *pr = ar;
if (ari == 1.0f)
for (j = pnn->cHidden; j; j--)
*pr++ += *prWeight++;
else if (ari == -1.0f)
for (j = pnn->cHidden; j; j--)
*pr++ -= *prWeight++;
else
for (j = pnn->cHidden; j; j--)
*pr++ += *prWeight++ * ari;
}
}
for (i = 0; i < pnn->cHidden; i++)
ar[i] = sigmoid(-pnn->rBetaHidden * ar[i]);
/* Calculate activity at output nodes */
prWeight = pnn->arOutputWeight;
for (i = 0; i < pnn->cOutput; i++) {
float r = pnn->arOutputThreshold[i];
for (j = 0; j < pnn->cHidden; j++)
r += ar[j] * *prWeight++;
arOutput[i] = sigmoid(-pnn->rBetaOutput * r);
}
}
extern int
NeuralNetEvaluate(const neuralnet * pnn, float arInput[], float arOutput[], NNState * pnState)
{
float *ar = (float *) g_alloca(pnn->cHidden * sizeof(float));
switch (NNevalAction(pnState)) {
case NNEVAL_NONE:
{
Evaluate(pnn, arInput, ar, arOutput, 0);
break;
}
case NNEVAL_SAVE:
{
pnState->cSavedIBase = pnn->cInput;
memcpy(pnState->savedIBase, arInput, pnn->cInput * sizeof(*ar));
Evaluate(pnn, arInput, ar, arOutput, pnState->savedBase);
break;
}
case NNEVAL_FROMBASE:
{
if (pnState->cSavedIBase != pnn->cInput) {
Evaluate(pnn, arInput, ar, arOutput, 0);
break;
}
memcpy(ar, pnState->savedBase, pnn->cHidden * sizeof(*ar));
{
float *r = arInput;
float *s = pnState->savedIBase;
unsigned int i;
for (i = 0; i < pnn->cInput; ++i, ++r, ++s) {
if (*r != *s /*lint --e(777) */ ) {
*r -= *s;
} else {
*r = 0.0;
}
}
}
EvaluateFromBase(pnn, arInput, ar, arOutput);
break;
}
}
return 0;
}
#endif
extern int
NeuralNetLoad(neuralnet * pnn, FILE * pf)
{
unsigned int i;
float *pr;
char dummy[16];
if (fscanf(pf, "%u %u %u %15s %f %f\n", &pnn->cInput, &pnn->cHidden,
&pnn->cOutput, dummy, &pnn->rBetaHidden,
&pnn->rBetaOutput) < 5 || pnn->cInput < 1 ||
pnn->cHidden < 1 || pnn->cOutput < 1 || pnn->rBetaHidden <= 0.0f || pnn->rBetaOutput <= 0.0f) {
errno = EINVAL;
return -1;
}
if (NeuralNetCreate(pnn, pnn->cInput, pnn->cHidden, pnn->cOutput, pnn->rBetaHidden, pnn->rBetaOutput))
return -1;
pnn->nTrained = 1;
for (i = pnn->cInput * pnn->cHidden, pr = pnn->arHiddenWeight; i; i--)
if (fscanf(pf, "%f\n", pr++) < 1)
return -1;
for (i = pnn->cHidden * pnn->cOutput, pr = pnn->arOutputWeight; i; i--)
if (fscanf(pf, "%f\n", pr++) < 1)
return -1;
for (i = pnn->cHidden, pr = pnn->arHiddenThreshold; i; i--)
if (fscanf(pf, "%f\n", pr++) < 1)
return -1;
for (i = pnn->cOutput, pr = pnn->arOutputThreshold; i; i--)
if (fscanf(pf, "%f\n", pr++) < 1)
return -1;
return 0;
}
extern int
NeuralNetLoadBinary(neuralnet * pnn, FILE * pf)
{
int dummy;
#define FREAD( p, c ) \
if ( fread( (p), sizeof( *(p) ), (c), pf ) < (unsigned int)(c) ) return -1
FREAD(&pnn->cInput, 1);
FREAD(&pnn->cHidden, 1);
FREAD(&pnn->cOutput, 1);
FREAD(&dummy, 1);
FREAD(&pnn->rBetaHidden, 1);
FREAD(&pnn->rBetaOutput, 1);
if (pnn->cInput < 1 || pnn->cHidden < 1 || pnn->cOutput < 1 || pnn->rBetaHidden <= 0.0f || pnn->rBetaOutput <= 0.0f) {
errno = EINVAL;
return -1;
}
if (NeuralNetCreate(pnn, pnn->cInput, pnn->cHidden, pnn->cOutput, pnn->rBetaHidden, pnn->rBetaOutput))
return -1;
pnn->nTrained = 1;
FREAD(pnn->arHiddenWeight, pnn->cInput * pnn->cHidden);
FREAD(pnn->arOutputWeight, pnn->cHidden * pnn->cOutput);
FREAD(pnn->arHiddenThreshold, pnn->cHidden);
FREAD(pnn->arOutputThreshold, pnn->cOutput);
#undef FREAD
return 0;
}
extern int
NeuralNetSaveBinary(const neuralnet * pnn, FILE * pf)
{
#define FWRITE( p, c ) \
if ( fwrite( (p), sizeof( *(p) ), (c), pf ) < (unsigned int)(c) ) return -1
FWRITE(&pnn->cInput, 1);
FWRITE(&pnn->cHidden, 1);
FWRITE(&pnn->cOutput, 1);
FWRITE(&pnn->nTrained, 1);
FWRITE(&pnn->rBetaHidden, 1);
FWRITE(&pnn->rBetaOutput, 1);
FWRITE(pnn->arHiddenWeight, pnn->cInput * pnn->cHidden);
FWRITE(pnn->arOutputWeight, pnn->cHidden * pnn->cOutput);
FWRITE(pnn->arHiddenThreshold, pnn->cHidden);
FWRITE(pnn->arOutputThreshold, pnn->cOutput);
#undef FWRITE
return 0;
}
#if defined(USE_SIMD_INSTRUCTIONS)
#if defined(DISABLE_SIMD_TEST)
int
SIMD_Supported(void)
{
return 1;
}
#else
#if defined(HAVE_SSE)
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <sys/sysctl.h>
#endif
static int
check_for_cpuid(void)
{
int result;
__asm__ __volatile__ (
#if defined(ENVIRONMENT32) && defined(__PIC__)
/* We have to be careful to not destroy ebx if using PIC on 32bit builds */
"pushl %%ebx\n\t"
#endif
"mov $1, %%eax\n\t"
"shl $21, %%eax\n\t"
"mov %%eax, %%edx\n\t"
#if defined(ENVIRONMENT64)
"pushf\n\t"
"pop %%rax\n\t"
#else
"pushfl\n\t"
"popl %%eax\n\t"
#endif
"mov %%eax, %%ecx\n\t"
"xor %%edx, %%eax\n\t"
#if defined(ENVIRONMENT64)
"push %%rax\n\t"
"popf\n\t"
"pushf\n\t"
"pop %%rax\n\t"
#else
"pushl %%eax\n\t"
"popfl\n\t"
"pushfl\n\t"
"popl %%eax\n\t"
#endif
"xor %%ecx, %%eax\n\t"
"test %%edx, %%eax\n\t"
"jnz cpuid_success\n\t"
/* Failed (non-pentium compatible machine) */
"mov $-1, %%eax\n\t"
"jmp cpuid_finished\n\t"
"cpuid_success:"
"xor %%eax, %%eax\n\t"
"cpuid\n\t"
"cmp $1, %%eax\n\t"
/* If 0 returned processor doesn't hav feature test */
"jge feature_success\n\t"
/* Unlucky - somehow cpuid 1 isn't supported */
"mov $-2, %%eax\n\t"
"jmp cpuid_finished\n\t"
"feature_success:"
"xor %%eax, %%eax\n\t"
"cpuid_finished:"
#if defined(ENVIRONMENT32) && defined(__PIC__)
"popl %%ebx\n\t"
#endif
: "=a"(result)
:
: "%ecx",
#if !defined(ENVIRONMENT32) || !defined(__PIC__)
"%ebx",
#endif
"%edx");
return result;
}
static int
CheckSSE(void)
{
int result;
int cpuidchk;
if ((cpuidchk = check_for_cpuid()) < 0)
return cpuidchk;
#if defined(USE_AVX)
__asm__ __volatile__ (
#if defined(ENVIRONMENT32) && defined(__PIC__)
/* We have to be careful to not destroy ebx if using PIC on 32bit builds */
"pushl %%ebx\n\t"
#endif
"mov $1, %%eax\n\t"
"cpuid\n\t"
"and $0x018000000, %%ecx\n\t" /* Check bit 27 (OS uses XSAVE/XRSTOR)*/
"cmp $0x018000000, %%ecx\n\t" /* and bit 28 (AVX supported by CPU) */
"jne avx_unsupported\n\t"
"xor %%ecx, %%ecx\n\t" /* XFEATURE_ENABLED_MASK/XCR0 register number = 0 */
"xgetbv\n\t" /* XFEATURE_ENABLED_MASK register is in edx:eax" */
"and $0b110, %%eax\n\t"
"cmp $0b110, %%eax\n\t" /* check the AVX registers restore at context switch */
"jne avx_unsupported\n\t"
"mov $1, %%eax\n\t"
"jmp avx_end\n\t"
"avx_unsupported:"
"xor %%eax, %%eax\n\t"
"avx_end:"
#if defined(ENVIRONMENT32) && defined(__PIC__)
"popl %%ebx\n\t"
#endif
: "=a"(result) /* Result returned in result variable */
:
: "%ecx",
#if !defined(ENVIRONMENT32) || !defined(__PIC__)
"%ebx",
#endif
"%edx"); /* These are all destroyed by cpuid */
#else
#if defined(__APPLE__) || defined(__FreeBSD__)
size_t length = sizeof(result);
#if defined(__APPLE__)
int error = sysctlbyname("hw.optional.sse", &result, &length, NULL, 0);
#endif
#if defined(__FreeBSD__)
int error = sysctlbyname("hw.instruction_sse", &result, &length, NULL, 0);
#endif
if (error != 0)
result = 0;
#else
__asm__ __volatile__ (
#if defined(ENVIRONMENT32) && defined(__PIC__)
/* We have to be careful to not destroy ebx if using PIC on I386 */
"pushl %%ebx\n\t"
#endif
/* Check if sse is supported (bit 25/26 in edx from cpuid 1) */
"mov $1, %%eax\n\t"
"cpuid\n\t"
"mov $1, %%eax\n\t"
#if defined(USE_SSE2)
"shl $26, %%eax\n\t"
#else
"shl $25, %%eax\n\t"
#endif
"test %%eax, %%edx\n\t"
"jnz sse_success\n\t"
/* Not supported */
"mov $0, %%eax\n\t"
"jmp sse_end\n\t"
"sse_success:"
/* Supported */
"mov $1, %%eax\n\t"
"sse_end:"
#if defined(ENVIRONMENT32) && defined(__PIC__)
"pop %%ebx\n\t"
#endif
: "=a"(result)
:
: "%ecx",
#if !defined(ENVIRONMENT32) || !defined(__PIC__)
"%ebx",
#endif
"%edx");
#endif /* APPLE/BSD */
#endif /* USE_AVX */
return result;
}
#endif /* HAVE_SSE */
int
SIMD_Supported(void)
{
static int state = -3;
if (state == -3) {
#if defined(HAVE_NEON)
state = CheckNEON();
#elif defined(HAVE_SSE)
state = CheckSSE();
#else
state = -2;
#endif
}
return state;
}
#endif
#endif
+89
View File
@@ -0,0 +1,89 @@
/*
* Copyright (C) 1999-2002 Gary Wong <gtw@gnu.org>
* Copyright (C) 2002-2017 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* $Id: neuralnet.h,v 1.36 2020/06/05 20:08:31 plm Exp $
*/
#ifndef NEURALNET_H
#define NEURALNET_H
#include <stdio.h>
#include "common.h"
typedef struct {
unsigned int cInput;
unsigned int cHidden;
unsigned int cOutput;
int nTrained;
float rBetaHidden;
float rBetaOutput;
float *arHiddenWeight;
float *arOutputWeight;
float *arHiddenThreshold;
float *arOutputThreshold;
} neuralnet;
typedef enum {
NNEVAL_NONE,
NNEVAL_SAVE,
NNEVAL_FROMBASE
} NNEvalType;
typedef enum {
NNSTATE_NONE = -1,
NNSTATE_INCREMENTAL,
NNSTATE_DONE
} NNStateType;
typedef struct {
NNStateType state;
float *savedBase;
float *savedIBase;
#if !defined(USE_SIMD_INSTRUCTIONS)
unsigned int cSavedIBase;
#endif
} NNState;
extern void NeuralNetDestroy(neuralnet * pnn);
#if !defined(USE_SIMD_INSTRUCTIONS)
extern int NeuralNetEvaluate(const neuralnet * pnn, float arInput[], float arOutput[], NNState * pnState);
#else
extern int NeuralNetEvaluateSSE(const neuralnet * pnn, float arInput[], float arOutput[], NNState * pnState);
#endif
extern int NeuralNetLoad(neuralnet * pnn, FILE * pf);
extern int NeuralNetLoadBinary(neuralnet * pnn, FILE * pf);
extern int NeuralNetSaveBinary(const neuralnet * pnn, FILE * pf);
extern int SIMD_Supported(void);
/* Try to determine whether we are 64-bit or 32-bit */
#if defined(_WIN32) || defined(_WIN64)
#if defined(_WIN64)
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#endif
#if defined(__GNUC__)
#if defined(__x86_64__)
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#endif
#endif
+583
View File
@@ -0,0 +1,583 @@
/*
* Copyright (C) 2006-2009 Jon Kinsey <jonkinsey@gmail.com>
* Copyright (C) 2007-2021 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "common.h"
#if defined(USE_SIMD_INSTRUCTIONS)
#define DEBUG_SSE 0
#include "simd.h"
#include "neuralnet.h"
#include <string.h>
#if defined(USE_NEON)
#include <arm_neon.h>
#elif defined(USE_AVX)
#include <immintrin.h>
#elif defined(USE_SSE2)
#include <emmintrin.h>
#else
#include <xmmintrin.h>
#endif
#include <glib.h>
#include "sigmoid.h"
#if defined(HAVE_NEON)
#include <signal.h>
#include <setjmp.h>
#endif
float *
sse_malloc(size_t size)
{
#if defined(HAVE_POSIX_MEMALIGN)
void *ptr = NULL;
int ret;
ret = posix_memalign(&ptr, ALIGN_SIZE, size);
if (ret == 0)
return (float *)ptr;
/* mimic g_malloc() error message */
g_error("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", G_STRLOC, size);
/* avoid warning: "control reaches end of non-void function" for
compilers that don't understand that g_error() never returns. */
return NULL;
#elif defined(HAVE__ALIGNED_MALLOC)
return (float *) _aligned_malloc(size, ALIGN_SIZE);
#else
return (float *) _mm_malloc(size, ALIGN_SIZE);
#endif
}
void
sse_free(float *ptr)
{
#if defined(HAVE_POSIX_MEMALIGN)
free(ptr);
#elif defined(HAVE__ALIGNED_MALLOC)
_aligned_free(ptr);
#else
_mm_free(ptr);
#endif
}
#if defined(HAVE_NEON)
static jmp_buf env;
static void
my_handler(int status)
{
longjmp(env, status);
}
int
CheckNEON(void)
{
void (*oldsig)(int);
oldsig = signal(SIGILL, my_handler);
if (setjmp(env)) {
signal(SIGILL, oldsig);
return 0;
} else {
#if defined(__ARM_FEATURE_SIMD32)
/* v7 */
__asm__ __volatile__ ("vmin.f32 q0, q0, q0");
#elif defined(__ARM_NEON)
/* aarch64 */
__asm__ __volatile__ ("fmin v0.4s, v0.4s, v0.4s");
#else
#error "Unexpected ARM architecture"
#endif
signal(SIGILL, oldsig);
return 1;
}
}
#endif
#if defined(USE_AVX) || defined(USE_SSE2) || defined(USE_NEON)
#include <stdint.h>
static const union {
float f[VEC_SIZE];
float_vector ps;
#if defined(USE_AVX)
} ones = { {
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
#else
} ones = { {
1.0f, 1.0f, 1.0f, 1.0f}};
#endif
static const union {
float f[VEC_SIZE];
float_vector ps;
#if defined(USE_AVX)
} tens = { {
10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f}};
#else
} tens = { {
10.0f, 10.0f, 10.0f, 10.0f}};
#endif
static const union {
int32_t i32[VEC_SIZE];
float_vector ps;
#if defined(USE_AVX)
} abs_mask = { {
0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF}};
#else
} abs_mask = { {
0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF}};
#endif
static inline float_vector
sigmoid_positive_ps(float_vector xin)
{
union {
int_vector i;
int32_t i32[VEC_SIZE];
} i;
float_vector ex;
float *ex_elem = (float *) &ex;
#if defined(USE_AVX)
float_vector x1 = _mm256_min_ps(xin, tens.ps);
#elif defined(HAVE_SSE)
float_vector x1 = _mm_min_ps(xin, tens.ps);
#else
float_vector x1 = vminq_f32(xin, tens.ps);
float_vector rec;
#endif
#if defined(USE_AVX)
x1 = _mm256_mul_ps(x1, tens.ps);
i.i = _mm256_cvttps_epi32(x1);
#elif defined(HAVE_SSE)
x1 = _mm_mul_ps(x1, tens.ps);
i.i = _mm_cvttps_epi32(x1);
#else
x1 = vmulq_f32(x1, tens.ps);
i.i = vcvtq_s32_f32(x1);
#endif
ex_elem[0] = e[i.i32[0]];
ex_elem[1] = e[i.i32[1]];
ex_elem[2] = e[i.i32[2]];
ex_elem[3] = e[i.i32[3]];
#if defined(USE_AVX)
ex_elem[4] = e[i.i32[4]];
ex_elem[5] = e[i.i32[5]];
ex_elem[6] = e[i.i32[6]];
ex_elem[7] = e[i.i32[7]];
#endif
#if defined(USE_AVX)
x1 = _mm256_sub_ps(x1, _mm256_cvtepi32_ps(i.i));
x1 = _mm256_add_ps(x1, tens.ps);
#if defined(USE_FMA3)
x1 = _mm256_fmadd_ps(x1, ex, ones.ps);
#else
x1 = _mm256_mul_ps(x1, ex);
x1 = _mm256_add_ps(x1, ones.ps);
#endif
#ifdef __FAST_MATH__
return _mm256_rcp_ps(x1);
#else
return _mm256_div_ps(ones.ps, x1);
#endif
#elif defined(HAVE_SSE)
x1 = _mm_sub_ps(x1, _mm_cvtepi32_ps(i.i));
x1 = _mm_add_ps(x1, tens.ps);
x1 = _mm_mul_ps(x1, ex);
x1 = _mm_add_ps(x1, ones.ps);
#ifdef __FAST_MATH__
return _mm_rcp_ps(x1);
#else
return _mm_div_ps(ones.ps, x1);
#endif
#else
x1 = vsubq_f32(x1, vcvtq_f32_s32(i.i));
x1 = vaddq_f32(x1, tens.ps);
x1 = vmulq_f32(x1, ex);
x1 = vaddq_f32(x1, ones.ps);
/* TODO: Check how many Newton-Raphson iterations are needed to match x86 rcp and div accuracy */
#ifdef __FAST_MATH__
rec = vrecpeq_f32(x1);
return vmulq_f32(vrecpsq_f32(x1, rec), rec);
#else
rec = vrecpeq_f32(x1);
rec = vmulq_f32(vrecpsq_f32(x1, rec), rec);
return vmulq_f32(vrecpsq_f32(x1, rec), rec);
#endif
#endif
}
static inline float_vector
sigmoid_ps(float_vector xin)
{
#if defined(USE_AVX)
float_vector mask = _mm256_cmp_ps(xin, _mm256_setzero_ps(), _CMP_LT_OS);
float_vector c;
xin = _mm256_and_ps(xin, abs_mask.ps); /* Abs. value by clearing signbit */
c = sigmoid_positive_ps(xin);
return _mm256_blendv_ps(c, _mm256_sub_ps(ones.ps, c), mask);
#elif defined(HAVE_SSE)
float_vector mask = _mm_cmplt_ps(xin, _mm_setzero_ps());
float_vector c;
xin = _mm_and_ps(xin, abs_mask.ps); /* Abs. value by clearing signbit */
c = sigmoid_positive_ps(xin);
/* _mm_blendv_ps() is only available with SSE4.1 or later */
return _mm_or_ps(_mm_andnot_ps(mask, c), _mm_and_ps(mask, _mm_sub_ps(ones.ps, c)));
#else
int_vector mask = (int_vector)vcltq_f32(xin, vdupq_n_f32(0.0f));
float_vector c;
xin = (float_vector)vandq_s32((int_vector)xin, (int_vector)abs_mask.ps); /* Abs. value by clearing signbit */
c = sigmoid_positive_ps(xin);
return vbslq_f32((uint32x4_t)mask, vsubq_f32(ones.ps, c), c);
#endif
}
#endif // USE_SSE2 or USE_AVX
#if defined(USE_SSE2)
#define INPUT_ADD() \
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
vec0 = _mm_load_ps(pr); \
vec1 = _mm_load_ps(prWeight); \
sum = _mm_add_ps(vec0, vec1); \
_mm_store_ps(pr, sum); \
}
#define INPUT_MULTADD() \
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
vec0 = _mm_load_ps(pr); \
vec1 = _mm_load_ps(prWeight); \
vec3 = _mm_mul_ps(vec1, scalevec); \
sum = _mm_add_ps(vec0, vec3); \
_mm_store_ps(pr, sum); \
}
#endif
#if defined(USE_AVX)
#define INPUT_ADD() \
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
vec0 = _mm256_load_ps(pr); \
vec1 = _mm256_load_ps(prWeight); \
sum = _mm256_add_ps(vec0, vec1); \
_mm256_store_ps(pr, sum); \
}
#if defined(USE_FMA3)
#define INPUT_MULTADD() \
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
vec0 = _mm256_load_ps(pr); \
vec1 = _mm256_load_ps(prWeight); \
sum = _mm256_fmadd_ps(vec1, scalevec, vec0); \
_mm256_store_ps(pr, sum); \
}
#else
#define INPUT_MULTADD() \
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
vec0 = _mm256_load_ps(pr); \
vec1 = _mm256_load_ps(prWeight); \
vec3 = _mm256_mul_ps(vec1, scalevec); \
sum = _mm256_add_ps(vec0, vec3); \
_mm256_store_ps(pr, sum); \
}
#endif
#endif
#if defined(USE_NEON)
#define INPUT_ADD() \
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
vec0 = vld1q_f32(pr); \
vec1 = vld1q_f32(prWeight); \
sum = vaddq_f32(vec0, vec1); \
vst1q_f32(pr, sum); \
}
#define INPUT_MULTADD() \
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
vec0 = vld1q_f32(pr); \
vec1 = vld1q_f32(prWeight); \
vec3 = vmulq_f32(vec1, scalevec); \
sum = vaddq_f32(vec0, vec3); \
vst1q_f32(pr, sum); \
}
#endif
static void
EvaluateSSE(const neuralnet * restrict pnn, const float arInput[], float ar[], float arOutput[])
{
const unsigned int cHidden = pnn->cHidden;
unsigned int i, j;
float *prWeight;
#if defined(USE_SSE2) || defined(USE_AVX) || defined(USE_NEON)
float *par;
#if defined(USE_FMA3)
float_vector vec0, vec1, scalevec, sum;
#else
float_vector vec0, vec1, vec3, scalevec, sum;
#endif
#endif
/* Calculate activity at hidden nodes */
memcpy(ar, pnn->arHiddenThreshold, cHidden * sizeof(float));
prWeight = pnn->arHiddenWeight;
if (pnn->cInput != 214) { /* everything but the racing net */
for (i = 0; i < 200;) { /* base inputs */
float ari = arInput[i++];
/* 3 binaries, 1 float */
if (likely(ari == 0.0f))
prWeight += cHidden;
else {
float *pr = ar;
INPUT_ADD();
}
ari = arInput[i++];
if (likely(ari == 0.0f))
prWeight += cHidden;
else {
float *pr = ar;
INPUT_ADD();
}
ari = arInput[i++];
if (likely(ari == 0.0f)) {
prWeight += cHidden;
/* If 3rd element is 0, so is 4th. Skip it */
prWeight += cHidden;
i++;
continue;
} else {
float *pr = ar;
INPUT_ADD();
}
ari = arInput[i++];
if (likely(ari == 0.0f))
prWeight += cHidden;
else {
float *pr = ar;
#if defined(USE_FMA3)
scalevec = _mm256_set1_ps(ari);
INPUT_MULTADD();
#elif defined(USE_NEON)
scalevec = vdupq_n_f32(ari);
INPUT_MULTADD();
#else
if (unlikely(ari == 1.0f)) {
INPUT_ADD();
} else {
#if defined(USE_AVX)
scalevec = _mm256_set1_ps(ari);
#elif defined(HAVE_SSE)
scalevec = _mm_set1_ps(ari);
#endif
INPUT_MULTADD();
}
#endif
} /* base inputs are done */
}
if (pnn->cInput == 250) /* Pruning nets are over, contact/crashed still have 2 * 25 floats */
for (i = 200; i < 250; i++) {
float const ari = arInput[i];
if (unlikely(ari == 0.0f))
prWeight += cHidden;
else {
float *pr = ar;
#if defined(USE_AVX)
scalevec = _mm256_set1_ps(ari);
#elif defined(HAVE_SSE)
scalevec = _mm_set1_ps(ari);
#else
scalevec = vdupq_n_f32(ari);
#endif
INPUT_MULTADD();
}
}
}
else /* racing net */
for (i = 0; i < pnn->cInput; i++) {
float const ari = arInput[i];
if (likely(ari == 0.0f))
prWeight += cHidden;
else {
float *pr = ar;
#if defined(USE_FMA3)
scalevec = _mm256_set1_ps(ari);
INPUT_MULTADD();
#elif defined(USE_NEON)
scalevec = vdupq_n_f32(ari);
INPUT_MULTADD();
#else
if (likely(ari == 1.0f)) {
INPUT_ADD();
} else {
#if defined(USE_AVX)
scalevec = _mm256_set1_ps(ari);
#elif defined(HAVE_SSE)
scalevec = _mm_set1_ps(ari);
#endif
INPUT_MULTADD();
}
#endif
}
}
#if defined(USE_SSE2) || defined(USE_AVX) || defined(USE_NEON)
#if defined(USE_AVX)
scalevec = _mm256_set1_ps(-pnn->rBetaHidden);
#elif defined(HAVE_SSE)
scalevec = _mm_set1_ps(-pnn->rBetaHidden);
#else
scalevec = vdupq_n_f32(-pnn->rBetaHidden);
#endif
for (par = ar, i = (cHidden >> LOG2VEC_SIZE); i; i--, par += VEC_SIZE) {
#if defined(USE_AVX)
float_vector vec = _mm256_load_ps(par);
vec = _mm256_mul_ps(vec, scalevec);
vec = sigmoid_ps(vec);
_mm256_store_ps(par, vec);
#elif defined(HAVE_SSE)
float_vector vec = _mm_load_ps(par);
vec = _mm_mul_ps(vec, scalevec);
vec = sigmoid_ps(vec);
_mm_store_ps(par, vec);
#else
float_vector vec = vld1q_f32(par);
vec = vmulq_f32(vec, scalevec);
vec = sigmoid_ps(vec);
vst1q_f32(par, vec);
#endif
}
#else
for (i = 0; i < cHidden; i++)
ar[i] = sigmoid(-pnn->rBetaHidden * ar[i]);
#endif
/* Calculate activity at output nodes */
prWeight = pnn->arOutputWeight;
for (i = 0; i < pnn->cOutput; i++) {
#if defined(USE_AVX)
SSE_ALIGN(float r[8]);
#else
float r;
#endif
float *pr = ar;
#if defined(USE_AVX)
sum = _mm256_setzero_ps();
#elif defined(HAVE_SSE)
sum = _mm_setzero_ps();
#else
sum = vdupq_n_f32(0.0f);
#endif
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, prWeight += VEC_SIZE, pr += VEC_SIZE) {
#if defined(USE_AVX)
vec0 = _mm256_load_ps(pr); /* Eight floats into vec0 */
vec1 = _mm256_load_ps(prWeight); /* Eight weights into vec1 */
#if defined(USE_FMA3)
sum = _mm256_fmadd_ps(vec0, vec1, sum);
#else
vec3 = _mm256_mul_ps(vec0, vec1); /* Multiply */
sum = _mm256_add_ps(sum, vec3); /* Add */
#endif
#elif defined(HAVE_SSE)
vec0 = _mm_load_ps(pr); /* Four floats into vec0 */
vec1 = _mm_load_ps(prWeight); /* Four weights into vec1 */
vec3 = _mm_mul_ps(vec0, vec1); /* Multiply */
sum = _mm_add_ps(sum, vec3); /* Add */
#else
vec0 = vld1q_f32(pr); /* Four floats into vec0 */
vec1 = vld1q_f32(prWeight); /* Four weights into vec1 */
vec3 = vmulq_f32(vec0, vec1); /* Multiply */
sum = vaddq_f32(sum, vec3); /* Add */
#endif
}
#if defined(USE_AVX)
vec0 = _mm256_hadd_ps(sum, sum);
vec1 = _mm256_hadd_ps(vec0, vec0);
_mm256_store_ps(r, vec1);
arOutput[i] = sigmoid(-pnn->rBetaOutput * (r[0] + r[4] + pnn->arOutputThreshold[i]));
#elif defined(HAVE_SSE)
vec0 = _mm_shuffle_ps(sum, sum, _MM_SHUFFLE(2, 3, 0, 1));
vec1 = _mm_add_ps(sum, vec0);
vec0 = _mm_shuffle_ps(vec1, vec1, _MM_SHUFFLE(1, 1, 3, 3));
sum = _mm_add_ps(vec1, vec0);
_mm_store_ss(&r, sum);
arOutput[i] = sigmoid(-pnn->rBetaOutput * (r + pnn->arOutputThreshold[i]));
#else
{
float32x2_t vec0_h, vec0_l, vec1;
vec0_h = vget_high_f32(sum);
vec0_l = vget_low_f32(sum);
vec1 = vpadd_f32(vec0_h, vec0_l);
vec1 = vpadd_f32(vec1, vec1);
vst1_lane_f32(&r, vec1, 0);
arOutput[i] = sigmoid(-pnn->rBetaOutput * (r + pnn->arOutputThreshold[i]));
}
#endif
}
#if defined(USE_AVX)
_mm256_zeroupper();
#endif
}
extern int
NeuralNetEvaluateSSE(const neuralnet * restrict pnn, /*lint -e{818} */ float arInput[],
float arOutput[], NNState * UNUSED(pnState))
{
SSE_ALIGN(float ar[pnn->cHidden]);
#if DEBUG_SSE
g_assert(sse_aligned(arOutput));
g_assert(sse_aligned(ar));
g_assert(sse_aligned(arInput));
#endif
EvaluateSSE(pnn, arInput, ar, arOutput);
return 0;
}
#endif
+168
View File
@@ -0,0 +1,168 @@
/*
* Copyright (C) 1998-2002 Gary Wong <gtw@gnu.org>
* Copyright (C) 2008-2017 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef SIGMOID_H
#define SIGMOID_H
/* e[k] = exp(k/10) / 10 */
static float e[101] = {
0.10000000000000001f,
0.11051709180756478f,
0.12214027581601698f,
0.13498588075760032f,
0.14918246976412702f,
0.16487212707001281f,
0.18221188003905089f,
0.20137527074704767f,
0.22255409284924679f,
0.245960311115695f,
0.27182818284590454f,
0.30041660239464335f,
0.33201169227365473f,
0.36692966676192446f,
0.40551999668446748f,
0.44816890703380646f,
0.49530324243951152f,
0.54739473917271997f,
0.60496474644129461f,
0.66858944422792688f,
0.73890560989306509f,
0.81661699125676512f,
0.90250134994341225f,
0.99741824548147184f,
1.1023176380641602f,
1.2182493960703473f,
1.3463738035001691f,
1.4879731724872838f,
1.6444646771097049f,
1.817414536944306f,
2.0085536923187668f,
2.2197951281441637f,
2.4532530197109352f,
2.7112638920657881f,
2.9964100047397011f,
3.3115451958692312f,
3.6598234443677988f,
4.0447304360067395f,
4.4701184493300818f,
4.9402449105530168f,
5.4598150033144233f,
6.034028759736195f,
6.6686331040925158f,
7.3699793699595784f,
8.1450868664968148f,
9.0017131300521811f,
9.9484315641933776f,
10.994717245212353f,
12.151041751873485f,
13.428977968493552f,
14.841315910257659f,
16.402190729990171f,
18.127224187515122f,
20.033680997479166f,
22.140641620418716f,
24.469193226422039f,
27.042640742615255f,
29.886740096706028f,
33.029955990964865f,
36.503746786532886f,
40.34287934927351f,
44.585777008251675f,
49.274904109325632f,
54.457191012592901f,
60.184503787208222f,
66.514163304436181f,
73.509518924197266f,
81.24058251675433f,
89.784729165041753f,
99.227471560502622f,
109.66331584284585f,
121.19670744925763f,
133.9430764394418f,
148.02999275845451f,
163.59844299959269f,
180.80424144560632f,
199.81958951041173f,
220.83479918872089f,
244.06019776244983f,
269.72823282685101f,
298.09579870417281f,
329.44680752838406f,
364.09503073323521f,
402.38723938223131f,
444.7066747699858f,
491.47688402991344f,
543.16595913629783f,
600.29122172610175f,
663.42440062778894f,
733.19735391559948f,
810.3083927575384f,
895.52927034825075f,
989.71290587439091f,
1093.8019208165192f,
1208.8380730216988f,
1335.9726829661872f,
1476.4781565577266f,
1631.7607198015421f,
1803.3744927828525f,
1993.0370438230298f,
1993.0370438230298f /* one extra :-) */
};
/* Calculate an approximation to the sigmoid function 1 / ( 1 + e^x ).
* This is executed very frequently during neural net evaluation, so
* careful optimisation here pays off.
*
* Statistics on sigmoid(x) calls:
* * >99% of the time, x is positive.
* * 82% of the time, 3 < abs(x) < 8.
*
* 02/2017: The numbers above are 10+ years old
* (old neural nets, possibly pruning nets not yet in use).
* Current stats :
* * 85% of the time, x is positive, comprising 80% < 10 and 20% > 10
* * 15% of the time, x is negative with 99%+ > -10
*/
static inline float
sigmoid(float const xin)
{
if (likely(xin >= 0.0f)) {
/* xin is almost always positive; we place this branch of the `if'
* first, in the hope that the compiler/processor will predict the
* conditional branch will not be taken. */
if (likely(xin < 10.0f)) {
/* again, predict the branch not to be taken */
const float x1 = 10.0f * xin;
const int i = (int) x1;
return 1.0f / (1.0f + e[i] * ((float)(10 - i) + x1));
} else
return 1.0f / 19931.370438230298f;
} else {
if (likely(xin > -10.0f)) {
const float x1 = -10.0f * xin;
const int i = (int) x1;
return 1.0f - 1.0f / (1 + e[i] * ((float)(10 - i) + x1));
} else
return 19930.370438230298f / 19931.370438230298f;
}
}
#endif
+93
View File
@@ -0,0 +1,93 @@
/*
* Copyright (C) 2005-2006 Jon Kinsey <jonkinsey@gmail.com>
* Copyright (C) 2005-2018 the AUTHORS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* $Id: simd.h,v 1.9 2018/05/12 21:42:33 plm Exp $
*/
#ifndef SIMD_H
#define SIMD_H
#if defined(USE_SIMD_INSTRUCTIONS)
#include <stdlib.h>
#include "common.h"
#if defined(USE_AVX)
#define ALIGN_SIZE 32
#define VEC_SIZE 8
#define LOG2VEC_SIZE 3
#define float_vector __m256
#define int_vector __m256i
#else
#define ALIGN_SIZE 16
#define VEC_SIZE 4
#define LOG2VEC_SIZE 2
#if defined(HAVE_SSE)
#define float_vector __m128
#define int_vector __m128i
#elif defined(HAVE_NEON)
#define float_vector float32x4_t
#define int_vector int32x4_t
/*
* Workaround for clang on raspbian 9
* It defines __ARM_FEATURE_SIMD32 but its arm_neon.h needs __ARM_NEON
*/
#define __ARM_NEON 1
#else
#error "Inconsistent SIMD defines"
#endif
#endif
#if defined(_MSC_VER)
#define SSE_ALIGN(D) __declspec(align(ALIGN_SIZE)) D
#else
#define SSE_ALIGN(D) D __attribute__ ((aligned(ALIGN_SIZE)))
#endif
#if defined(__GNUC__) && defined(WIN32)
/* Align stack pointer on 16 byte boundary so SSE variables work correctly */
#define SIMD_STACKALIGN __attribute__((force_align_arg_pointer))
#if defined(USE_AVX)
#define SIMD_AVX_STACKALIGN __attribute__((force_align_arg_pointer))
#else
#define SIMD_AVX_STACKALIGN
#endif /* USE_AVX */
#else
#define SIMD_STACKALIGN
#define SIMD_AVX_STACKALIGN
#endif /* GNUC/WIN32 */
#define sse_aligned(ar) (!(((size_t)ar) % ALIGN_SIZE))
extern float *sse_malloc(size_t size);
extern void sse_free(float *ptr);
#if defined(HAVE_NEON)
extern int CheckNEON(void);
#endif
#else /* USE_SIMD_INSTRUCTIONS */
#define SSE_ALIGN(D) D
#define sse_malloc malloc
#define sse_free free
#define SIMD_STACKALIGN
#define SIMD_AVX_STACKALIGN
#endif
#endif /* SIMD_H */