#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
#include <cgo.h>
#include "argph.h"
#include "def.h"
typedef struct {
pid_t pid;
- int input_read;
- int input_write;
- int output_read;
- int output_write;
+ int input[2];
+ int output[2];
} ngspice_process;
task_def taskg;
+// ngspice .func replacements have some undesirable
+// behaviours, gcc macros used instead.
char *macros = \
"#define Minimize(expr) let __ngsolve_f=__ngsolve_f+expr\n"
- "#define Maximize(expr) Minimize(-expr)\n"
+ "#define Maximize(expr) let __ngsolve_f=__ngsolve_f-(expr)\n"
"#define Constrain(expr) let __ngsolve_c=__ngsolve_c+expr\n"
- "#define Lt(a, b) max(exp(a - b) - 1, 0)\n"
+ "#define Lt(a, b) Constrain(max(0, exp(a - b) - 1))\n"
"#define Gt(a, b) Lt(b, a)\n"
"#define Band(v, lo, hi) Lt(lo, v) + Lt(v, hi)\n";
char *readstream(int fd) {
int s = 128;
+ int r;
char *buf = malloc(s * 2);
- if (read(fd, buf, s) != s) return buf;
+ if ((r = read(fd, buf, s)) != s) {
+ *(buf + r) = 0;
+ return buf;
+ };
char *pos = buf + s;
- while (read(fd, pos, s) == s) {
+ while ((r = read(fd, pos, s)) == s) {
buf = realloc(buf, (s *= 2) * 2);
pos = buf + s;
}
+ *(pos + r) = 0;
return buf;
}
void load_netlist(task_def *task) {
- FILE *netlist = fopen(taskg.netlist_file, "r");
+ FILE *netlist = fopen( task->netlist_file, "r");
char *netlist_raw = readfile(netlist);
fclose(netlist);
int in[2];
close(out[0]);
dup2(in[0], STDIN_FILENO);
dup2(out[1], STDOUT_FILENO);
- if (execlp("gcc", "gcc", "-E", "-P", "-x", "c", "-", NULL)) {
+ if (execlp("gcc", "gcc", "-E", "-P", "-x", "c", "/dev/fd/0", NULL)) {
printf("ngsolve: FATAL EXCEPTION\n gcc invocation unsuccessful. Check that a working binary is added to PATH as `gcc`.");
exit(1);
}
}
void spawn_ngspice(ngspice_process *chp) {
- if (pipe(&chp->input_read) || pipe(&chp->output_read)) {
+ if (pipe(chp->input) || pipe(chp->output)) {
printf("ngsolve: FATAL EXCEPTION\n pipe creation unsuccessful.");
exit(1);
}
pid_t p = fork();
if (!p) {
- close(chp->input_write);
- close(chp->output_read);
- // STDOUT is not linked to chp->output_write as this
- // would pollute the pipe with ngspice logs.
- dup2(chp->input_read, STDIN_FILENO);
+ int null_fd = open("/dev/null", O_WRONLY);
+ close(chp->input[1]);
+ close(chp->output[0]);
+ dup2(chp->input[0], STDIN_FILENO);
+ dup2(null_fd, STDOUT_FILENO);
+ dup2(null_fd, STDERR_FILENO);
// Convert to an ngspice process;
// execlp does not (!) return to caller if successful.
- if (execlp("ngspice", "ngspice", "<&0", NULL)) {
+ if (execlp("ngspice", "ngspice", "-b", "/dev/fd/0", NULL)) {
printf("ngsolve: FATAL EXCEPTION\n ngspice invocation unsuccessful. Check that a working binary is added to PATH as `ngspice`.");
exit(1);
}
}
- close(chp->input_read);
- close(chp->output_write);
+ close(chp->input[0]);
+ close(chp->output[1]);
chp->pid = p;
}
double fitness(double *x) {
ngspice_process chp;
spawn_ngspice(&chp);
- dprintf(chp.input_write, "ngsolve\n");
+ dprintf(chp.input[1], "ngsolve\n\n.CONTROL\nlet __ngsolve_f=0\nlet __ngsolve_c=0\n.ENDC\n");
for (size_t d = 0; d < taskg.dvarc; d++)
- dprintf(chp.input_write, ".PARAM %s=%f\n", taskg.dvars[d].symbol, x[d]);
+ // .PARAM lines are used by circuit definition but cannot be used in
+ // .CONTROL blocks. .CSPARAM lines vice-versa (therefore both necessary).
+ dprintf(chp.input[1], ".PARAM %s=%f\n.CSPARAM %s=%s\n", taskg.dvars[d].symbol, x[d], taskg.dvars[d].symbol, taskg.dvars[d].symbol);
// Note: ngspice manual specifies input files have to end on ".END\n".
// This seems to not be enforced, so this control statement can be appended after it.
- dprintf(chp.input_write, "*%s\n.CONTROL\necho \"$&__ngsolve_c $&__ngsolve_f\" >&%d\nexec %d>&-\n.ENDC\n", taskg.netlist, chp.output_write, chp.output_write);
- close(chp.input_write); // Done writing (writes EOF to ngspice on chp.input_write).
- char *outp = readstream(chp.output_read);
- close(chp.output_read);
+ dprintf(chp.input[1], "*%s\n.CONTROL\necho \"$&__ngsolve_c $&__ngsolve_f\" >> /dev/fd/%d\n.ENDC\n.END\n", taskg.netlist, chp.output[1]);
+ close(chp.input[1]); // Done writing (writes EOF to ngspice on chp.input_write).
+ char *outp = readstream(chp.output[0]);
+ close(chp.output[0]);
char *pos = outp;
double constraints = strtod(pos, &pos);
double fitness = strtod(pos, &pos);
return constraints + fitness;
}
-void optimize() {
- double *sample_range_min = malloc(sizeof(double) * taskg.dvarc);
- double *sample_range_max = malloc(sizeof(double) * taskg.dvarc);
- for (size_t d = 0; d < taskg.dvarc; d++) {
- sample_range_min[d] = taskg.dvars[d].sample_min;
- sample_range_max[d] = taskg.dvars[d].sample_max;
+void optimize(task_def *task) {
+ double *sample_range_min = malloc(sizeof(double) * task->dvarc);
+ double *sample_range_max = malloc(sizeof(double) * task->dvarc);
+ for (size_t d = 0; d < task->dvarc; d++) {
+ sample_range_min[d] = task->dvars[d].sample_min;
+ sample_range_max[d] = task->dvars[d].sample_max;
}
- load_netlist(&taskg);
cgo_de_parameters par = {
- taskg.dvarc,
- taskg.agentc,
+ task->dvarc,
+ task->agentc,
sample_range_min,
sample_range_max,
fitness,
0.9,
0.8
};
- double *res = malloc(sizeof(double) * taskg.dvarc);
+ double *res = malloc(sizeof(double) * task->dvarc);
cgo_de(&par, res);
printf("ngsolve: converged at x=(");
- for (size_t d = 0; d < taskg.dvarc - 1; d++) printf("%s=%f, ", taskg.dvars[d].symbol, res[d]);
- printf("%s=%f) with f(x)=%f\n", taskg.dvars[taskg.dvarc - 1].symbol, res[taskg.dvarc - 1], fitness(res));
+ for (size_t d = 0; d < task->dvarc - 1; d++) printf("%s=%f, ", task->dvars[d].symbol, res[d]);
+ printf("%s=%f) with f(x)=%f\n", task->dvars[task->dvarc - 1].symbol, res[task->dvarc - 1], fitness(res));
}
int main(int argc, char *argv[]) {
struct arguments args;
argp_parse(&argp, argc, argv, 0, 0, &args);
+ design_var dvars[] = {
+ {
+ "a",
+ -10,
+ 10
+ },
+ {
+ "b",
+ -10,
+ 10
+ }
+ };
+ taskg.netlist_file = "test.ngs";
+ taskg.agentc = 10;
+ taskg.dvarc = 2;
+ taskg.dvars = dvars;
+ load_netlist(&taskg);
+ optimize(&taskg);
return 0;
}