mirror of
https://frontier.innolan.net/rainlance/amiga-ntimed.git
synced 2025-11-21 11:13:09 +00:00
First preview release.
This commit is contained in:
216
combine_delta.c
Normal file
216
combine_delta.c
Normal file
@ -0,0 +1,216 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 Poul-Henning Kamp
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Source Combiner based on delta-pdfs
|
||||
* ===================================
|
||||
*
|
||||
* The basic principle here is that sources gives us four values:
|
||||
* - The highest low value were the probability is zero.
|
||||
* - The lowest high value were the probability is zero.
|
||||
* - The most probable value
|
||||
* - The relative trust in that value [0...1]
|
||||
* Together this defines a triangular probability density function.
|
||||
*
|
||||
* The combiner adds all these pdfs' together weighted by trust
|
||||
* and finds the highest probability which sports a quorum.
|
||||
*
|
||||
* See also: http://phk.freebsd.dk/time/20141107.html
|
||||
*
|
||||
* XXX: decay trust by age
|
||||
* XXX: auto determine quorom if param not set
|
||||
* XXX: param for minimum probability density
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ntimed.h"
|
||||
|
||||
struct combine_delta;
|
||||
|
||||
struct cd_stat {
|
||||
double x;
|
||||
double prob;
|
||||
unsigned quorum;
|
||||
};
|
||||
|
||||
struct cd_source {
|
||||
unsigned magic;
|
||||
#define CD_SOURCE_MAGIC 0x2799775c
|
||||
TAILQ_ENTRY(cd_source) list;
|
||||
|
||||
struct combiner combiner;
|
||||
|
||||
struct combine_delta *cd;
|
||||
double trust, low, mid, high;
|
||||
int tb_gen;
|
||||
};
|
||||
|
||||
struct combine_delta {
|
||||
unsigned magic;
|
||||
#define COMBINE_DELTA_MAGIC 0x8dc5030c
|
||||
|
||||
unsigned nsrc;
|
||||
TAILQ_HEAD(, cd_source) head;
|
||||
};
|
||||
|
||||
struct combine_delta *
|
||||
CD_New(void)
|
||||
{
|
||||
struct combine_delta *cd;
|
||||
|
||||
ALLOC_OBJ(cd, COMBINE_DELTA_MAGIC);
|
||||
AN(cd);
|
||||
|
||||
TAILQ_INIT(&cd->head);
|
||||
return (cd);
|
||||
}
|
||||
|
||||
static void
|
||||
cd_try_peak(const struct combine_delta *cd, double *mx, double *my, double x,
|
||||
struct cd_stat *st)
|
||||
{
|
||||
struct cd_source *cs;
|
||||
|
||||
// XXX: Hack to make plots with log zscale and only one
|
||||
// XXX: source look sensible.
|
||||
st->x = x;
|
||||
st->prob = 0.001;
|
||||
st->quorum = 0;
|
||||
|
||||
TAILQ_FOREACH(cs, &cd->head, list) {
|
||||
if (cs->tb_gen != TB_generation)
|
||||
continue;
|
||||
if (x < cs->low)
|
||||
continue;
|
||||
if (x > cs->high)
|
||||
continue;
|
||||
if (cs->low >= cs->high)
|
||||
continue;
|
||||
|
||||
st->quorum++;
|
||||
if (x < cs->mid) {
|
||||
st->prob += cs->trust * 2.0 * (x - cs->low) /
|
||||
((cs->high - cs->low) * (cs->mid - cs->low));
|
||||
} else {
|
||||
st->prob += cs->trust * 2.0 * (cs->high - x) /
|
||||
((cs->high - cs->low) * (cs->high - cs->mid));
|
||||
}
|
||||
if (isnan(st->prob)) {
|
||||
Fail(NULL, 0, "lo %.3e hi %.3e mid %.3e",
|
||||
cs->low, cs->high, cs->mid);
|
||||
}
|
||||
}
|
||||
if (st->prob > *my) {
|
||||
*my = st->prob;
|
||||
*mx = x;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
stat_cmp(const void *p1, const void *p2)
|
||||
{
|
||||
const struct cd_stat *left = p1;
|
||||
const struct cd_stat *right = p2;
|
||||
|
||||
/*lint -save -e514 */
|
||||
return ((left->x > right->x) - (left->x < right->x));
|
||||
/*lint -restore */
|
||||
}
|
||||
|
||||
static void
|
||||
cd_find_peak(struct ocx *ocx, const struct combine_delta *cd)
|
||||
{
|
||||
struct cd_source *cs;
|
||||
double max_x = 0;
|
||||
double max_y = 1;
|
||||
struct cd_stat st[cd->nsrc * 3L];
|
||||
size_t m;
|
||||
|
||||
m = 0;
|
||||
TAILQ_FOREACH(cs, &cd->head, list) {
|
||||
if (cs->tb_gen != TB_generation)
|
||||
continue;
|
||||
cd_try_peak(cd, &max_x, &max_y, cs->low, &st[m++]);
|
||||
cd_try_peak(cd, &max_x, &max_y, cs->mid, &st[m++]);
|
||||
cd_try_peak(cd, &max_x, &max_y, cs->high, &st[m++]);
|
||||
}
|
||||
Put(ocx, OCX_TRACE,
|
||||
" %.3e %.3e %.3e\n", max_x, max_y, log(max_y)/log(10.));
|
||||
PLL(ocx, max_x, max_y);
|
||||
qsort(st, cd->nsrc * 3L, sizeof st[0], stat_cmp);
|
||||
}
|
||||
|
||||
static void __match_proto__(combine_f)
|
||||
cd_filter(struct ocx *ocx, const struct combiner *cb,
|
||||
double trust, double low, double mid, double high)
|
||||
{
|
||||
struct cd_source *cs;
|
||||
struct combine_delta *cd;
|
||||
|
||||
CHECK_OBJ_NOTNULL(cb, COMBINER_MAGIC);
|
||||
CAST_OBJ_NOTNULL(cs, cb->priv, CD_SOURCE_MAGIC);
|
||||
cd = cs->cd;
|
||||
CHECK_OBJ_NOTNULL(cd, COMBINE_DELTA_MAGIC);
|
||||
|
||||
/* Sign: local - remote -> postive is ahead */
|
||||
assert(trust >= 0 && trust <= 1.0);
|
||||
cs->trust = trust;
|
||||
cs->low = low;
|
||||
cs->mid = mid;
|
||||
cs->high = high;
|
||||
cs->tb_gen = TB_generation;
|
||||
|
||||
Put(ocx, OCX_TRACE,
|
||||
"Combine %s %s %.6f %.6f %.6f", cb->name1, cb->name2,
|
||||
cs->low, cs->mid, cs->high);
|
||||
|
||||
cd_find_peak(ocx, cd);
|
||||
}
|
||||
|
||||
struct combiner *
|
||||
CD_AddSource(struct combine_delta *cd, const char *name1, const char *name2)
|
||||
{
|
||||
struct cd_source *cs;
|
||||
|
||||
CHECK_OBJ_NOTNULL(cd, COMBINE_DELTA_MAGIC);
|
||||
|
||||
ALLOC_OBJ(cs, CD_SOURCE_MAGIC);
|
||||
AN(cs);
|
||||
cs->cd = cd;
|
||||
cs->low = cs->mid = cs->high = NAN;
|
||||
TAILQ_INSERT_TAIL(&cd->head, cs, list);
|
||||
|
||||
INIT_OBJ(&cs->combiner, COMBINER_MAGIC);
|
||||
cs->combiner.func = cd_filter;
|
||||
cs->combiner.priv = cs;
|
||||
cs->combiner.name1 = name1;
|
||||
cs->combiner.name2 = name2;
|
||||
|
||||
cd->nsrc += 1;
|
||||
|
||||
return (&cs->combiner);
|
||||
}
|
||||
Reference in New Issue
Block a user