ngspice/src/analysis/cktpzstr.c

1201 lines
26 KiB
C

/**********
Copyright 1990 Regents of the University of California. All rights reserved.
**********/
/*
* A variant on the "zeroin" method. This is a bit convoluted.
*/
#include "ngspice.h"
#include <stdio.h>
#include "pzdefs.h"
#include "complex.h"
#include "cktdefs.h"
#include "devdefs.h"
#include "sperror.h"
#ifdef PZDEBUG
# ifndef notdef
# define DEBUG(N) if (Debug >= (unsigned) (N))
static unsigned int Debug = 1;
# else
# define DEBUG(N) if (0)
# endif
#endif
/* static function definitions */
static int CKTpzStrat(PZtrial **set);
static int CKTpzStep(int strat, PZtrial **set);
static int CKTpzRunTrial(CKTcircuit *ckt, PZtrial **new_trialp, PZtrial **set);
static int CKTpzVerify(PZtrial **set, PZtrial *new_trial);
static void clear_trials(int mode);
static void check_flat(PZtrial *a, PZtrial *b);
void CKTpzUpdateSet(PZtrial **set, PZtrial *new);
void zaddeq(double *a, int *amag, double x, int xmag, double y, int ymag);
void CKTpzReset(PZtrial **set);
#ifdef PZDEBUG
static void show_trial( );
#endif
#define NITER_LIM 200
#define SHIFT_LEFT 2
#define SHIFT_RIGHT 3
#define SKIP_LEFT 4
#define SKIP_RIGHT 5
#define INIT 6
#define GUESS 7
#define SPLIT_LEFT 8
#define SPLIT_RIGHT 9
#define MULLER 10
#define SYM 11
#define SYM2 12
#define COMPLEX_INIT 13
#define COMPLEX_GUESS 14
#define QUIT 15
#define NEAR_LEFT 4
#define MID_LEFT 5
#define FAR_LEFT 6
#define NEAR_RIGHT 7
#define FAR_RIGHT 8
#define MID_RIGHT 9
#ifdef PZDEBUG
static char *snames[ ] = {
"none",
"none",
"shift left",
"shift right",
"skip left",
"skip right",
"init",
"guess",
"split left",
"split right",
"Muller",
"sym 1",
"sym 2",
"complex_init",
"complex_guess",
"quit",
"none"
};
#endif
#define sgn(X) ((X) < 0 ? -1 : (X) == 0 ? 0 : 1)
#define ISAROOT 2
#define ISAREPEAT 4
#define ISANABERRATION 8
#define ISAMINIMA 16
extern double NIpzK;
extern int NIpzK_mag;
int CKTpzTrapped;
static int NZeros, NFlat, Max_Zeros;
static PZtrial *ZeroTrial, *Trials;
static int Seq_Num;
static double Guess_Param;
static double High_Guess, Low_Guess;
static int Last_Move, Consec_Moves;
static int NIter, NTrials;
static int Aberr_Num;
int PZeval(int strat, PZtrial **set, PZtrial **new_trial_p);
static PZtrial *pzseek(PZtrial *t, int dir);
static int alter(PZtrial *new, PZtrial *nearto, double abstol, double reltol);
int
CKTpzFindZeros(CKTcircuit *ckt, PZtrial **rootinfo, int *rootcount)
{
PZtrial *new_trial;
PZtrial *neighborhood[3];
int strat;
int error;
char ebuf[513];
NIpzK = 0.0;
NIpzK_mag = 0;
High_Guess = -1.0;
Low_Guess = 1.0;
ZeroTrial = 0;
Trials = 0;
NZeros = 0;
NFlat = 0;
Max_Zeros = SMPmatSize(ckt->CKTmatrix);
NIter = 0;
error = OK;
CKTpzTrapped = 0;
Aberr_Num = 0;
NTrials = 0;
ckt->CKTniState |= NIPZSHOULDREORDER; /* Initial for LU fill-ins */
Seq_Num = 1;
CKTpzReset(neighborhood);
do {
while ((strat = CKTpzStrat(neighborhood)) < GUESS && !CKTpzTrapped)
if (!CKTpzStep(strat, neighborhood)) {
strat = GUESS;
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "\t\tGuess\n");
#endif
break;
}
NIter += 1;
/* Evaluate current strategy */
error = PZeval(strat, neighborhood, &new_trial);
if (error != OK)
return error;
error = CKTpzRunTrial(ckt, &new_trial, neighborhood);
if (error != OK)
return error;
if (new_trial->flags & ISAROOT) {
if (CKTpzVerify(neighborhood, new_trial)) {
NIter = 0;
CKTpzReset(neighborhood);
} else
/* XXX Verify fails ?!? */
CKTpzUpdateSet(neighborhood, new_trial);
} else if (new_trial->flags & ISANABERRATION) {
CKTpzReset(neighborhood);
Aberr_Num += 1;
free(new_trial);
} else if (new_trial->flags & ISAMINIMA) {
neighborhood[0] = NULL;
neighborhood[1] = new_trial;
neighborhood[2] = NULL;
} else {
CKTpzUpdateSet(neighborhood, new_trial); /* Replace a value */
}
if ((*(SPfrontEnd->IFpauseTest))( )) {
sprintf(ebuf,
"Pole-Zero analysis interrupted; %d trials, %d roots\n",
Seq_Num, NZeros);
(*(SPfrontEnd->IFerror))(ERR_WARNING, ebuf, 0);
error = E_PAUSE;
break;
}
} while (High_Guess - Low_Guess < 1e40
&& NZeros < Max_Zeros
&& NIter < NITER_LIM && Aberr_Num < 3
&& High_Guess - Low_Guess < 1e35 /* XXX Should use mach const */
&& (!neighborhood[0] || !neighborhood[2] || CKTpzTrapped
|| neighborhood[2]->s.real - neighborhood[0]->s.real < 1e22));
/* XXX ZZZ */
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr,
"Finished: NFlat %d, NZeros: %d, NTrials %d, Guess %g to %g, aber %d\n",
NFlat, NZeros, NTrials, Low_Guess, High_Guess, Aberr_Num);
#endif
if (NZeros >= Seq_Num - 1) {
/* Short */
clear_trials(ISAROOT);
*rootinfo = NULL;
*rootcount = 0;
ERROR(E_SHORT, "The input signal is shorted on the way to the output");
} else
clear_trials(0);
*rootinfo = Trials;
*rootcount = NZeros;
if (Aberr_Num > 2) {
sprintf(ebuf,
"Pole-zero converging to numerical aberrations; giving up after %d trials",
Seq_Num);
(*(SPfrontEnd->IFerror))(ERR_WARNING, ebuf, 0);
}
if (NIter >= NITER_LIM) {
sprintf(ebuf,
"Pole-zero iteration limit reached; giving up after %d trials",
Seq_Num);
(*(SPfrontEnd->IFerror))(ERR_WARNING, ebuf, 0);
}
return error;
}
/* PZeval: evaluate an estimation function (given by 'strat') for the next
guess (returned in a PZtrial) */
/* XXX ZZZ */
int
PZeval(int strat, PZtrial **set, PZtrial **new_trial_p)
{
int error;
PZtrial *new_trial;
new_trial = NEW(PZtrial);
new_trial->multiplicity = 0;
new_trial->count = 0;
new_trial->seq_num = Seq_Num++;
switch (strat) {
case GUESS:
if (High_Guess < Low_Guess)
Guess_Param = 0.0;
else if (Guess_Param > 0.0) {
if (High_Guess > 0.0)
Guess_Param = High_Guess * 10.0;
else
Guess_Param = 1.0;
} else {
if (Low_Guess < 0.0)
Guess_Param = Low_Guess * 10.0;
else
Guess_Param = -1.0;
}
if (Guess_Param > High_Guess)
High_Guess = Guess_Param;
if (Guess_Param < Low_Guess)
Low_Guess = Guess_Param;
new_trial->s.real = Guess_Param;
if (set[1])
new_trial->s.imag = set[1]->s.imag;
else
new_trial->s.imag = 0.0;
error = OK;
break;
case SYM:
case SYM2:
error = NIpzSym(set, new_trial);
if (CKTpzTrapped == 1) {
if (new_trial->s.real < set[0]->s.real
|| new_trial->s.real > set[1]->s.real) {
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr,
"FIXED UP BAD Strat: %s (%d) was (%.15g,%.15g)\n",
snames[strat], CKTpzTrapped,
new_trial->s.real, new_trial->s.imag);
#endif
new_trial->s.real = (set[0]->s.real + set[1]->s.real) / 2.0;
}
} else if (CKTpzTrapped == 2) {
if (new_trial->s.real < set[1]->s.real
|| new_trial->s.real > set[2]->s.real) {
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr,
"FIXED UP BAD Strat: %s (%d) was (%.15g,%.15g)\n",
snames[strat], CKTpzTrapped,
new_trial->s.real, new_trial->s.imag);
#endif
new_trial->s.real = (set[1]->s.real + set[2]->s.real) / 2.0;
}
} else if (CKTpzTrapped == 3) {
if ((new_trial->s.real <= set[0]->s.real
|| new_trial->s.real == set[1]->s.real)
&& (new_trial->s.imag == set[1]->s.imag
|| new_trial->s.real >= set[2]->s.real)) {
#ifdef PZDEBUG
DEBUG(1)
fprintf(stderr,
"FIXED UP BAD Strat: %s (%d), was (%.15g %.15g)\n",
snames[strat], CKTpzTrapped,
new_trial->s.real, new_trial->s.imag);
#endif
new_trial->s.real = (set[0]->s.real + set[2]->s.real) / 2.0;
if (new_trial->s.real == set[1]->s.real) {
#ifdef PZDEBUG
DEBUG(1)
fprintf(stderr, "Still off!");
#endif
if (Last_Move == MID_LEFT || Last_Move == NEAR_RIGHT)
new_trial->s.real = (set[0]->s.real + set[1]->s.real)
/ 2.0;
else
new_trial->s.real = (set[1]->s.real + set[2]->s.real)
/ 2.0;
}
}
}
break;
case COMPLEX_INIT:
/* Not automatic */
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "\tPZ minima at: %-30g %d\n",
NIpzK, NIpzK_mag);
#endif
new_trial->s.real = set[1]->s.real;
/* NIpzK is a good idea, but the value gets trashed
* due to the numerics when zooming in on a minima.
* The key is to know when to stop taking new values for NIpzK
* (which I don't). For now I take the first value indicated
* by the NIpzSym2 routine. A "hack".
*/
if (NIpzK != 0.0 && NIpzK_mag > -10) {
while (NIpzK_mag > 0) {
NIpzK *= 2.0;
NIpzK_mag -= 1;
}
while (NIpzK_mag < 0) {
NIpzK /= 2.0;
NIpzK_mag += 1;
}
new_trial->s.imag = NIpzK;
} else
new_trial->s.imag = 10000.0;
/*
* Reset NIpzK so the same value doesn't get used again.
*/
NIpzK = 0.0;
NIpzK_mag = 0;
error = OK;
break;
case COMPLEX_GUESS:
if (!set[2]) {
new_trial->s.real = set[0]->s.real;
new_trial->s.imag = 1.0e8;
} else {
new_trial->s.real = set[0]->s.real;
new_trial->s.imag = 1.0e12;
}
error = OK;
break;
case MULLER:
error = NIpzMuller(set, new_trial);
break;
case SPLIT_LEFT:
new_trial->s.real = (set[0]->s.real + 2 * set[1]->s.real) / 3.0;
error = OK;
break;
case SPLIT_RIGHT:
new_trial->s.real = (set[2]->s.real + 2 * set[1]->s.real) / 3.0;
error = OK;
break;
default:
ERROR(E_PANIC, "Step type unkown");
break;
}
*new_trial_p = new_trial;
return error;
}
/* CKTpzStrat: given three points, determine a good direction or method for
guessing the next zero */
/* XXX ZZZ what is a strategy for complex hunting? */
int CKTpzStrat(PZtrial **set)
{
int suggestion;
double a, b;
int a_mag, b_mag;
double k1, k2;
int new_trap;
new_trap = 0;
if (set[1] && (set[1]->flags & ISAMINIMA)) {
suggestion = COMPLEX_INIT;
} else if (set[0] && set[0]->s.imag != 0.0) {
if (!set[1] || !set[2])
suggestion = COMPLEX_GUESS;
else
suggestion = MULLER;
} else if (!set[0] || !set[1] || !set[2]) {
suggestion = INIT;
} else {
if (sgn(set[0]->f_def.real) != sgn(set[1]->f_def.real)) {
/* Zero crossing between s[0] and s[1] */
new_trap = 1;
suggestion = SYM2;
} else if (sgn(set[1]->f_def.real) != sgn(set[2]->f_def.real)) {
/* Zero crossing between s[1] and s[2] */
new_trap = 2;
suggestion = SYM2;
} else {
zaddeq(&a, &a_mag, set[1]->f_def.real, set[1]->mag_def,
-set[0]->f_def.real, set[0]->mag_def);
zaddeq(&b, &b_mag, set[2]->f_def.real, set[2]->mag_def,
-set[1]->f_def.real, set[1]->mag_def);
if (!CKTpzTrapped) {
k1 = set[1]->s.real - set[0]->s.real;
k2 = set[2]->s.real - set[1]->s.real;
if (a_mag + 10 < set[0]->mag_def
&& a_mag + 10 < set[1]->mag_def
&& b_mag + 10 < set[1]->mag_def
&& b_mag + 10 < set[2]->mag_def) {
if (k1 > k2)
suggestion = SKIP_RIGHT;
else
suggestion = SKIP_LEFT;
} else if (sgn(a) != -sgn(b)) {
if (a == 0.0)
suggestion = SKIP_LEFT;
else if (b == 0.0)
suggestion = SKIP_RIGHT;
else if (sgn(a) == sgn(set[1]->f_def.real))
suggestion = SHIFT_LEFT;
else
suggestion = SHIFT_RIGHT;
} else if (sgn(a) == -sgn(set[1]->f_def.real)) {
new_trap = 3;
/* minima in magnitude above the x axis */
/* Search for exact mag. minima, look for complex pair */
suggestion = SYM;
} else if (k1 > k2)
suggestion = SKIP_RIGHT;
else
suggestion = SKIP_LEFT;
} else {
new_trap = 3; /* still */
/* XXX ? Are these tests needed or is SYM safe all the time? */
if (sgn(a) != sgn(b)) {
/* minima in magnitude */
/* Search for exact mag. minima, look for complex pair */
suggestion = SYM;
} else if ((a_mag > b_mag || a_mag == b_mag)
&& fabs(a) > fabs(b))
suggestion = SPLIT_LEFT;
else
suggestion = SPLIT_RIGHT;
}
}
if (Consec_Moves >= 3 && CKTpzTrapped == new_trap) {
new_trap = CKTpzTrapped;
if (Last_Move == MID_LEFT || Last_Move == NEAR_RIGHT)
suggestion = SPLIT_LEFT;
else if (Last_Move == MID_RIGHT || Last_Move == NEAR_LEFT)
suggestion = SPLIT_RIGHT;
else
abort( ); /* XXX */
Consec_Moves = 0;
}
}
CKTpzTrapped = new_trap;
#ifdef PZDEBUG
DEBUG(1) {
if (set[0] && set[1] && set[2])
fprintf(stderr, "given %.15g %.15g / %.15g %.15g / %.15g %.15g\n",
set[0]->s.real, set[0]->s.imag, set[1]->s.real, set[1]->s.imag,
set[2]->s.real, set[2]->s.imag);
fprintf(stderr, "suggestion(%d/%d/%d | %d): %s\n",
NFlat, NZeros, Max_Zeros, CKTpzTrapped, snames[suggestion]);
}
#endif
return suggestion;
}
/* CKTpzRunTrial: eval the function at a given 's', fold in deflation */
int
CKTpzRunTrial(CKTcircuit *ckt, PZtrial **new_trialp, PZtrial **set)
{
PZtrial *match, *base, *new_trial;
PZtrial *p, *prev;
SPcomplex def_frac, diff_frac;
double reltol, abstol;
int def_mag, diff_mag, error;
int i;
int pretest, shifted, was_shifted;
int repeat;
new_trial = *new_trialp;
if (new_trial->s.imag < 0.0)
new_trial->s.imag *= -1.0;
/* Insert the trial into the list of Trials, while calculating
the deflation factor from previous zeros */
pretest = 0;
shifted = 0;
repeat = 0;
do {
def_mag = 0;
def_frac.real = 1.0;
def_frac.imag = 0.0;
was_shifted = shifted;
shifted = 0;
prev = NULL;
base = NULL;
match = NULL;
for (p = Trials; p != NULL; p = p->next) {
C_SUBEQ(diff_frac,p->s,new_trial->s);
if ((diff_frac.real < 0.0
|| diff_frac.real == 0.0) && diff_frac.imag < 0.0) {
prev = p;
if (p->flags & ISAMINIMA)
base = p;
}
if (p->flags & ISAROOT) {
abstol = 1e-5;
reltol = 1e-6;
} else {
abstol = 1e-20;
reltol = 1e-12;
}
if (diff_frac.imag == 0.0 &&
fabs(diff_frac.real) / (fabs(p->s.real) + abstol/reltol)
< reltol) {
#ifdef PZDEBUG
DEBUG(1) {
fprintf(stderr,
"diff_frac.real = %10g, p->s = %10g, nt = %10g\n",
diff_frac.real, p->s.real, new_trial->s.real);
fprintf(stderr, "ab=%g,rel=%g\n", abstol, reltol);
}
#endif
if (was_shifted || p->count >= 3
|| !alter(new_trial, set[1], abstol, reltol)) {
/* assume either a root or minima */
p->count = 0;
pretest = 1;
break;
} else
p->count += 1; /* try to shift */
shifted = 1; /* Re-calculate deflation */
break;
} else {
if (!CKTpzTrapped)
p->count = 0;
if (p->flags & ISAROOT) {
diff_mag = 0;
C_NORM(diff_frac,diff_mag);
if (diff_frac.imag != 0.0) {
C_MAG2(diff_frac);
diff_mag *= 2;
}
C_NORM(diff_frac,diff_mag);
for (i = p->multiplicity; i > 0; i--) {
C_MUL(def_frac,diff_frac);
def_mag += diff_mag;
C_NORM(def_frac,def_mag);
}
} else if (!match)
match = p;
}
}
} while (shifted);
if (pretest) {
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "Pre-test taken\n");
/* XXX Should catch the double-zero right off
* if K is 0.0
* instead of forcing a re-converge */
DEBUG(1) {
fprintf(stderr, "NIpzK == %g, mag = %d\n", NIpzK, NIpzK_mag);
fprintf(stderr, "over at %.30g %.30g (new %.30g %.30g, %x)\n",
p->s.real, p->s.imag, new_trial->s.real, new_trial->s.imag,
p->flags);
}
#endif
if (!(p->flags & ISAROOT) && CKTpzTrapped == 3
&& NIpzK != 0.0 && NIpzK_mag > -10) {
#ifdef notdef
if (p->flags & ISAROOT) {
/* Ugh! muller doesn't work right */
new_trial->flags = ISAMINIMA;
new_trial->s.imag = scalb(NIpzK, (int) (NIpzK_mag / 2));
pretest = 0;
} else {
#endif
p->flags |= ISAMINIMA;
free(new_trial);
*new_trialp = p;
repeat = 1;
} else if (p->flags & ISAROOT) {
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "Repeat at %.30g %.30g\n",
p->s.real, p->s.imag);
#endif
*new_trialp = p;
p->flags |= ISAREPEAT;
p->multiplicity += 1;
repeat = 1;
} else {
/* Regular zero, as precise as we can get it */
error = E_SINGULAR;
}
}
if (!repeat) {
if (!pretest) {
/* Run the trial */
ckt->CKTniState |= NIPZSHOULDREORDER; /* XXX */
if (!(ckt->CKTniState & NIPZSHOULDREORDER)) {
CKTpzLoad(ckt, &new_trial->s);
#ifdef PZDEBUG
DEBUG(3) {
printf("Original:\n");
SMPprint(ckt->CKTmatrix, stdout);
}
#endif
error = SMPcLUfac(ckt->CKTmatrix, ckt->CKTpivotAbsTol);
if (error == E_SINGULAR) {
#ifdef PZDEBUG
DEBUG(1) printf("Needs reordering\n");
#endif
ckt->CKTniState |= NIPZSHOULDREORDER;
} else if (error != OK)
return error;
}
if (ckt->CKTniState & NIPZSHOULDREORDER) {
CKTpzLoad(ckt, &new_trial->s);
error = SMPcReorder(ckt->CKTmatrix, 1.0e-30,
0.0 /* 0.1 Piv. Rel. */,
&(((PZAN *) ckt->CKTcurJob)->PZnumswaps));
}
if (error != E_SINGULAR) {
ckt->CKTniState &= ~NIPZSHOULDREORDER;
#ifdef PZDEBUG
DEBUG(3) {
printf("Factored:\n");
SMPprint(ckt->CKTmatrix, stdout);
}
#endif
error = SMPcDProd(ckt->CKTmatrix, &new_trial->f_raw,
&new_trial->mag_raw);
}
}
if ((error == E_SINGULAR || new_trial->f_raw.real == 0.0)
&& new_trial->f_raw.imag == 0.0) {
new_trial->f_raw.real = 0.0;
new_trial->f_raw.imag = 0.0;
new_trial->mag_raw = 0;
new_trial->f_def.real = 0.0;
new_trial->f_def.imag = 0.0;
new_trial->mag_def = 0;
new_trial->flags = ISAROOT;
/*printf("SMP Det: Singular\n");*/
} else if (error != OK)
return error;
else {
/* PZnumswaps is either 0 or 1 */
new_trial->f_raw.real *= ((PZAN *) ckt->CKTcurJob)->PZnumswaps;
new_trial->f_raw.imag *= ((PZAN *) ckt->CKTcurJob)->PZnumswaps;
/*
printf("SMP Det: (%g,%g)^%d\n", new_trial->f_raw.real,
new_trial->f_raw.imag, new_trial->mag_raw);
*/
new_trial->f_def.real = new_trial->f_raw.real;
new_trial->f_def.imag = new_trial->f_raw.imag;
new_trial->mag_def = new_trial->mag_raw;
C_DIV(new_trial->f_def,def_frac);
new_trial->mag_def -= def_mag;
C_NORM(new_trial->f_def,new_trial->mag_def);
}
/* Link into the rest of the list */
if (prev) {
new_trial->next = prev->next;
if (prev->next)
prev->next->prev = new_trial;
prev->next = new_trial;
} else {
if (Trials)
Trials->prev = new_trial;
else
ZeroTrial = new_trial;
new_trial->next = Trials;
Trials = new_trial;
}
new_trial->prev = prev;
NTrials += 1;
if (!(new_trial->flags & ISAROOT)) {
if (match)
check_flat(match, new_trial);
else
NFlat = 1;
}
}
#ifdef PZDEBUG
show_trial(new_trial, '*');
#endif
return OK;
}
/* Process a zero; inc. zero count, deflate other trials */
int
CKTpzVerify(PZtrial **set, PZtrial *new_trial)
{
PZtrial *next;
int diff_mag;
SPcomplex diff_frac;
double tdiff;
PZtrial *t, *prev;
NZeros += 1;
if (new_trial->s.imag != 0.0)
NZeros += 1;
NFlat = 0;
if (new_trial->multiplicity == 0) {
new_trial->flags |= ISAROOT;
new_trial->multiplicity = 1;
}
prev = NULL;
for (t = Trials; t; t = next) {
next = t->next;
if (t->flags & ISAROOT) {
prev = t;
/* Don't need to bother */
continue;
}
C_SUBEQ(diff_frac,new_trial->s,t->s);
if (new_trial->s.imag != 0.0)
C_MAG2(diff_frac);
tdiff = diff_frac.real;
/* Note that Verify is called for each time the root is found, so
* multiplicity is not significant
*/
if (diff_frac.real != 0.0) {
diff_mag = 0;
C_NORM(diff_frac,diff_mag);
diff_mag *= -1;
C_DIV(t->f_def,diff_frac);
C_NORM(t->f_def,diff_mag);
t->mag_def += diff_mag;
}
if (t->s.imag != 0.0
|| fabs(tdiff) / (fabs(new_trial->s.real) + 200) < 0.005) {
if (prev)
prev->next = t->next;
if (t->next)
t->next->prev = prev;
NTrials -= 1;
#ifdef PZDEBUG
show_trial(t, '-');
#endif
if (t == ZeroTrial) {
if (t->next)
ZeroTrial = t->next;
else if (t->prev)
ZeroTrial = t->prev;
else
ZeroTrial = NULL;
}
if (t == Trials) {
Trials = t->next;
}
free(t);
} else {
if (prev)
check_flat(prev, t);
else
NFlat = 1;
if (t->flags & ISAMINIMA)
t->flags &= ~ISAMINIMA;
prev = t;
#ifdef PZDEBUG
show_trial(t, '+');
#endif
}
}
return 1; /* always ok */
}
/* pzseek: search the trial list (given a starting point) for the first
* non-zero entry; direction: -1 for prev, 1 for next, 0 for next
* -or- first. Also, sets "Guess_Param" at the next reasonable
* value to guess at if the search falls of the end of the list
*/
static PZtrial *
pzseek(PZtrial *t, int dir)
{
Guess_Param = dir;
if (t == NULL)
return NULL;
if (dir == 0 && !(t->flags & ISAROOT) && !(t->flags & ISAMINIMA))
return t;
do {
if (dir >= 0)
t = t->next;
else
t = t->prev;
} while (t && ((t->flags & ISAROOT) || (t->flags & ISAMINIMA)));
return t;
}
static void
clear_trials(int mode)
{
PZtrial *t, *next, *prev;
prev = NULL;
for (t = Trials; t; t = next) {
next = t->next;
if (mode || !(t->flags & ISAROOT)) {
free(t);
} else {
if (prev)
prev->next = t;
else
Trials = t;
t->prev = prev;
prev = t;
}
}
if (prev)
prev->next = NULL;
else
Trials = NULL;
}
void
CKTpzUpdateSet(PZtrial **set, PZtrial *new)
{
int this_move;
this_move = 0;
if (new->s.imag != 0.0) {
set[2] = set[1];
set[1] = set[0];
set[0] = new;
} else if (!set[1])
set[1] = new;
else if (!set[2] && new->s.real > set[1]->s.real) {
set[2] = new;
} else if (!set[0]) {
set[0] = new;
} else if (new->flags & ISAMINIMA) {
set[1] = new;
} else if (new->s.real < set[0]->s.real) {
set[2] = set[1];
set[1] = set[0];
set[0] = new;
this_move = FAR_LEFT;
} else if (new->s.real < set[1]->s.real) {
if ((!CKTpzTrapped || new->mag_def < set[1]->mag_def
|| new->mag_def == set[1]->mag_def)
&& fabs(new->f_def.real) < fabs(set[1]->f_def.real)) {
/* Really should check signs, not just compare fabs( ) */
set[2] = set[1]; /* XXX = set[2]->prev :: possible opt */
set[1] = new;
this_move = MID_LEFT;
} else {
set[0] = new;
this_move = NEAR_LEFT;
}
} else if (new->s.real < set[2]->s.real) {
if ((!CKTpzTrapped || new->mag_def < set[1]->mag_def
|| new->mag_def == set[1]->mag_def)
&& fabs(new->f_def.real) < fabs(set[1]->f_def.real)) {
/* Really should check signs, not just compare fabs( ) */
set[0] = set[1];
set[1] = new;
this_move = MID_RIGHT;
} else {
set[2] = new;
this_move = NEAR_RIGHT;
}
} else {
set[0] = set[1];
set[1] = set[2];
set[2] = new;
this_move = FAR_RIGHT;
}
if (CKTpzTrapped && this_move == Last_Move)
Consec_Moves += 1;
else
Consec_Moves = 0;
Last_Move = this_move;
}
void
zaddeq(double *a, int *amag, double x, int xmag, double y, int ymag)
{
/* Balance magnitudes . . . */
if (xmag > ymag) {
*amag = xmag;
if (xmag > 50 + ymag)
y = 0.0;
else
for (xmag -= ymag; xmag > 0; xmag--)
y /= 2.0;
} else {
*amag = ymag;
if (ymag > 50 + xmag)
x = 0.0;
else
for (ymag -= xmag; ymag > 0; ymag--)
x /= 2.0;
}
*a = x + y;
if (*a == 0.0)
*amag = 0;
else {
while (fabs(*a) > 1.0) {
*a /= 2.0;
*amag += 1;
}
while (fabs(*a) < 0.5) {
*a *= 2.0;
*amag -= 1;
}
}
}
#ifdef PZDEBUG
static void
show_trial(new_trial, x)
PZtrial *new_trial;
char x;
{
DEBUG(1) fprintf(stderr, "%c (%3d/%3d) %.15g %.15g :: %.30g %.30g %d\n", x,
NIter, new_trial->seq_num, new_trial->s.real, new_trial->s.imag,
new_trial->f_def.real, new_trial->f_def.imag, new_trial->mag_def);
DEBUG(1)
if (new_trial->flags & ISANABERRATION) {
fprintf(stderr, "*** numerical aberration ***\n");
}
}
#endif
static void
check_flat(PZtrial *a, PZtrial *b)
{
int diff_mag;
SPcomplex diff_frac;
double mult;
diff_mag = a->mag_def - b->mag_def;
if (abs(diff_mag) <= 1) {
if (diff_mag == 1)
mult = 2.0;
else if (diff_mag == -1)
mult = 0.5;
else
mult = 1.0;
C_SUBEQ(diff_frac, mult * a->f_def, b->f_def);
C_MAG2(diff_frac);
if (diff_frac.real < 1.0e-20)
NFlat += 1;
}
/* XXX else NFlat = ?????? */
}
/* XXX ZZZ */
int
CKTpzStep(int strat, PZtrial **set)
{
switch (strat) {
case INIT:
if (!set[1]) {
set[1] = pzseek(ZeroTrial, 0);
} else if (!set[2])
set[2] = pzseek(set[1], 1);
else if (!set[0])
set[0] = pzseek(set[1], -1);
break;
case SKIP_LEFT:
set[0] = pzseek(set[0], -1);
break;
case SKIP_RIGHT:
set[2] = pzseek(set[2], 1);
break;
case SHIFT_LEFT:
set[2] = set[1];
set[1] = set[0];
set[0] = pzseek(set[0], -1);
break;
case SHIFT_RIGHT:
set[0] = set[1];
set[1] = set[2];
set[2] = pzseek(set[2], 1);
break;
}
if (!set[0] || !set[1] || !set[2])
return 0;
else
return 1;
}
void
CKTpzReset(PZtrial **set)
{
CKTpzTrapped = 0;
Consec_Moves = 0;
set[1] = pzseek(ZeroTrial, 0);
if (set[1] != NULL) {
set[0] = pzseek(set[1], -1);
set[2] = pzseek(set[1], 1);
} else {
set[0] = NULL;
set[2] = NULL;
}
}
static int
alter(PZtrial *new, PZtrial *nearto, double abstol, double reltol)
{
double p1, p2;
#ifdef PZDEBUG
DEBUG(1) {
fprintf(stderr, "ALTER from: %.30g %.30g\n",
new->s.real, new->s.imag);
if (nearto->prev)
fprintf(stderr, "nt->prev %g\n", nearto->prev->s.real);
if (nearto->next)
fprintf(stderr, "nt->next %g\n", nearto->next->s.real);
}
#endif
if (CKTpzTrapped != 2) {
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "not 2\n");
#endif
p1 = nearto->s.real;
if (nearto->flags & ISAROOT)
p1 -= 1e-6 * nearto->s.real + 1e-5;
if (nearto->prev) {
p1 += nearto->prev->s.real;
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "p1 %g\n", p1);
#endif
} else
p1 -= 10.0 * (fabs(p1) + 1.0);
p1 /= 2.0;
} else
p1 = nearto->s.real;
if (CKTpzTrapped != 1) {
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "not 1\n");
#endif
p2 = nearto->s.real;
if (nearto->flags & ISAROOT)
p2 += 1e-6 * nearto->s.real + 1e-5; /* XXX Would rather use pow(2)*/
if (nearto->next) {
p2 += nearto->next->s.real;
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "p2 %g\n", p2);
#endif
} else
p2 += 10.0 * (fabs(p2)+ 1.0);
p2 /= 2.0;
} else
p2 = nearto->s.real;
if ((nearto->prev &&
fabs(p1 - nearto->prev->s.real) /
fabs(nearto->prev->s.real) + abstol/reltol < reltol)
||
(nearto->next &&
fabs(p2 - nearto->next->s.real) /
fabs(nearto->next->s.real) + abstol/reltol < reltol)) {
#ifdef PZDEBUG
DEBUG(1)
fprintf(stderr, "Bailed out\n");
#endif
return 0;
}
if (CKTpzTrapped != 2 && nearto->s.real - p1 > p2 - nearto->s.real) {
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "take p1\n");
#endif
new->s.real = p1;
} else {
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "take p2\n");
#endif
new->s.real = p2;
}
#ifdef PZDEBUG
DEBUG(1) fprintf(stderr, "ALTER to : %.30g %.30g\n",
new->s.real, new->s.imag);
#endif
return 1;
}