Skip to content
Snippets Groups Projects
Commit 45f2ddd9 authored by dawehr's avatar dawehr
Browse files

Merge branch 'controller-network'

Conflicts:
	quad/sw/modular_quad_pid/src/control_algorithm.c
	quad/sw/modular_quad_pid/src/log_data.c
parents e3631bbe b4c74e30
No related branches found
No related tags found
No related merge requests found
Showing
with 1056 additions and 0 deletions
# Always use lf for c and header files
*.c eol=lf
*.h eol=lf
......@@ -11,3 +11,7 @@ make || exit 1
cd $QUAD_ROOT/sw/modular_quad_pid/test
make || exit 1
./test_uart_buff || exit 1
cd $QUAD_ROOT/computation_graph/test
make || exit 1
./test_computation_graph || exit 1
bin/
build/
computation_graph
test/test_computation_graph
#### PROJECT SETTINGS ####
# The name of the executable to be created
BIN_NAME := computation_graph
# Compiler used
CC ?= gcc
# Extension of source files used in the project
SRC_EXT = c
# Path to the source directory, relative to the makefile
SRC_PATH = ./src
# Space-separated pkg-config libraries used by this project
LIBS =
# General compiler flags
COMPILE_FLAGS = -std=c99 -Wall -Wextra -g
# Additional release-specific flags
RCOMPILE_FLAGS = -D NDEBUG
# Additional debug-specific flags
DCOMPILE_FLAGS = -D DEBUG
# Add additional include paths
INCLUDES = -I $(SRC_PATH)
# General linker settings
LINK_FLAGS = -lm
# Additional release-specific linker settings
RLINK_FLAGS =
# Additional debug-specific linker settings
DLINK_FLAGS =
# Destination directory, like a jail or mounted system
DESTDIR = /
# Install path (bin/ is appended automatically)
INSTALL_PREFIX = usr/local
#### END PROJECT SETTINGS ####
# Generally should not need to edit below this line
# Obtains the OS type, either 'Darwin' (OS X) or 'Linux'
UNAME_S:=$(shell uname -s)
# Function used to check variables. Use on the command line:
# make print-VARNAME
# Useful for debugging and adding features
print-%: ; @echo $*=$($*)
# Shell used in this makefile
# bash is used for 'echo -en'
SHELL = /bin/bash
# Clear built-in rules
.SUFFIXES:
# Programs for installation
INSTALL = install
INSTALL_PROGRAM = $(INSTALL)
INSTALL_DATA = $(INSTALL) -m 644
# Append pkg-config specific libraries if need be
ifneq ($(LIBS),)
COMPILE_FLAGS += $(shell pkg-config --cflags $(LIBS))
LINK_FLAGS += $(shell pkg-config --libs $(LIBS))
endif
# Verbose option, to output compile and link commands
export V := false
export CMD_PREFIX := @
ifeq ($(V),true)
CMD_PREFIX :=
endif
# Combine compiler and linker flags
release: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(RCOMPILE_FLAGS)
release: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(RLINK_FLAGS)
debug: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(DCOMPILE_FLAGS)
debug: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(DLINK_FLAGS)
# Build and output paths
release: export BUILD_PATH := build/release
release: export BIN_PATH := bin/release
debug: export BUILD_PATH := build/debug
debug: export BIN_PATH := bin/debug
install: export BIN_PATH := bin/release
# Find all source files in the source directory, sorted by most
# recently modified
ifeq ($(UNAME_S),Darwin)
SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' | sort -k 1nr | cut -f2-)
else
SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' -printf '%T@\t%p\n' \
| sort -k 1nr | cut -f2-)
endif
# fallback in case the above fails
rwildcard = $(foreach d, $(wildcard $1*), $(call rwildcard,$d/,$2) \
$(filter $(subst *,%,$2), $d))
ifeq ($(SOURCES),)
SOURCES := $(call rwildcard, $(SRC_PATH), *.$(SRC_EXT))
endif
# Set the object file names, with the source directory stripped
# from the path, and the build path prepended in its place
OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o)
# Set the dependency files that will be used to add header dependencies
DEPS = $(OBJECTS:.o=.d)
# Macros for timing compilation
ifeq ($(UNAME_S),Darwin)
CUR_TIME = awk 'BEGIN{srand(); print srand()}'
TIME_FILE = $(dir $@).$(notdir $@)_time
START_TIME = $(CUR_TIME) > $(TIME_FILE)
END_TIME = read st < $(TIME_FILE) ; \
$(RM) $(TIME_FILE) ; \
st=$$((`$(CUR_TIME)` - $$st)) ; \
echo $$st
else
TIME_FILE = $(dir $@).$(notdir $@)_time
START_TIME = date '+%s' > $(TIME_FILE)
END_TIME = read st < $(TIME_FILE) ; \
$(RM) $(TIME_FILE) ; \
st=$$((`date '+%s'` - $$st - 86400)) ; \
echo `date -u -d @$$st '+%H:%M:%S'`
endif
# Version macros
# Comment/remove this section to remove versioning
USE_VERSION := false
# If this isn't a git repo or the repo has no tags, git describe will return non-zero
ifeq ($(shell git describe > /dev/null 2>&1 ; echo $$?), 0)
USE_VERSION := true
VERSION := $(shell git describe --tags --long --dirty --always | \
sed 's/v\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)-\?.*-\([0-9]*\)-\(.*\)/\1 \2 \3 \4 \5/g')
VERSION_MAJOR := $(word 1, $(VERSION))
VERSION_MINOR := $(word 2, $(VERSION))
VERSION_PATCH := $(word 3, $(VERSION))
VERSION_REVISION := $(word 4, $(VERSION))
VERSION_HASH := $(word 5, $(VERSION))
VERSION_STRING := \
"$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH).$(VERSION_REVISION)-$(VERSION_HASH)"
override CFLAGS := $(CFLAGS) \
-D VERSION_MAJOR=$(VERSION_MAJOR) \
-D VERSION_MINOR=$(VERSION_MINOR) \
-D VERSION_PATCH=$(VERSION_PATCH) \
-D VERSION_REVISION=$(VERSION_REVISION) \
-D VERSION_HASH=\"$(VERSION_HASH)\"
endif
# Standard, non-optimized release build
.PHONY: release
release: dirs
ifeq ($(USE_VERSION), true)
@echo "Beginning release build v$(VERSION_STRING)"
else
@echo "Beginning release build"
endif
@$(START_TIME)
@$(MAKE) all --no-print-directory
@echo -n "Total build time: "
@$(END_TIME)
# Debug build for gdb debugging
.PHONY: debug
debug: dirs
ifeq ($(USE_VERSION), true)
@echo "Beginning debug build v$(VERSION_STRING)"
else
@echo "Beginning debug build"
endif
@$(START_TIME)
@$(MAKE) all --no-print-directory
@echo -n "Total build time: "
@$(END_TIME)
# Create the directories used in the build
.PHONY: dirs
dirs:
@echo "Creating directories"
@mkdir -p $(dir $(OBJECTS))
@mkdir -p $(BIN_PATH)
# Installs to the set path
.PHONY: install
install:
@echo "Installing to $(DESTDIR)$(INSTALL_PREFIX)/bin"
@$(INSTALL_PROGRAM) $(BIN_PATH)/$(BIN_NAME) $(DESTDIR)$(INSTALL_PREFIX)/bin
# Uninstalls the program
.PHONY: uninstall
uninstall:
@echo "Removing $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)"
@$(RM) $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)
# Removes all build files
.PHONY: clean
clean:
@echo "Deleting $(BIN_NAME) symlink"
@$(RM) $(BIN_NAME)
@echo "Deleting directories"
@$(RM) -r build
@$(RM) -r bin
# Main rule, checks the executable and symlinks to the output
all: $(BIN_PATH)/$(BIN_NAME)
@echo "Making symlink: $(BIN_NAME) -> $<"
@$(RM) $(BIN_NAME)
@ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME)
# Link the executable
$(BIN_PATH)/$(BIN_NAME): $(OBJECTS)
@echo "Linking: $@"
@$(START_TIME)
$(CMD_PREFIX)$(CC) $(OBJECTS) $(LDFLAGS) -o $@
@echo -en "\t Link time: "
@$(END_TIME)
# Add dependency files, if they exist
-include $(DEPS)
# Source file rules
# After the first compilation they will be joined with the rules from the
# dependency files to provide header dependencies
$(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT)
@echo "Compiling: $< -> $@"
@$(START_TIME)
$(CMD_PREFIX)$(CC) $(CFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@
@echo -en "\t Compile time: "
@$(END_TIME)
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "computation_graph.h"
#define GRAPH_MAX_DEPTH 20
#define GRAPH_MAX_INPUTS 20
// Array to store input values for passing to the execute function of each node
static double exec_input_vals[GRAPH_MAX_INPUTS];
struct computation_graph *create_graph() {
struct computation_graph *the_graph = malloc(sizeof(struct computation_graph));
if (!the_graph) {return NULL;}
the_graph->nodes = malloc(sizeof(struct graph_node));
if (!the_graph->nodes) { return NULL; }
the_graph->n_nodes = 0;
the_graph->size = 1;
return the_graph;
}
static void reset_node_rec(struct computation_graph* graph, int node_id, int depth) {
if (depth > GRAPH_MAX_DEPTH) {
return;
}
struct graph_node* node = &graph->nodes[node_id];
// Don't reset nodes that are already connected to something else
// Don't reset nodes that have already been reset or discovered
if (node->n_children != 1 || node->processed_state != UNPROCESSED) {
return;
}
node->processed_state = DISCOVERED;
int input_id;
for (input_id = 0; input_id < node->type->n_inputs; input_id++) {
int src_node_id = node->input_srcs[input_id].controller_id;
if (src_node_id != -1) {
reset_node_rec(graph, src_node_id, depth + 1);
}
}
// Reset this node
if (node->type->reset != NULL) {
node->type->reset(node->state);
}
node->updated = 1;
node->processed_state = PROCESSED;
}
int reset_node(struct computation_graph* graph, int node_id) {
if (node_id >= graph->n_nodes) {
return -1;
}
int i;
for (i = 0; i < graph->n_nodes; i++) {
graph->nodes[i].processed_state = UNPROCESSED;
}
reset_node_rec(graph, node_id, 0);
return 0;
}
int graph_set_source(struct computation_graph *graph,
int dest_node_id, int dest_input, int src_node_id, int src_output) {
if (dest_node_id >= graph->n_nodes || src_node_id >= graph->n_nodes) {
return -1;
}
struct graph_node *dest_node = &graph->nodes[dest_node_id];
struct graph_node *src_node = &graph->nodes[src_node_id];
if (dest_input >= dest_node->type->n_inputs || src_output >= src_node->type->n_outputs) {
return -1;
}
// If a previous source exists, remove one from its children count
int prev_src_id = dest_node->input_srcs[dest_input].controller_id;
if (prev_src_id != -1) {
graph->nodes[prev_src_id].n_children -= 1;
}
src_node->n_children += 1; // Count destination node as a child
dest_node->input_srcs[dest_input].controller_id = src_node_id;
dest_node->input_srcs[dest_input].controller_output = src_output;
dest_node->updated = 1;
reset_node(graph, src_node_id);
return 0;
}
int graph_add_node(struct computation_graph *graph,
const char* name,
const struct graph_node_type *type,
void *state) {
assert(type->n_inputs <= GRAPH_MAX_INPUTS);
int new_id = graph->n_nodes;
if (new_id >= graph->size) {
int new_capacity = graph->n_nodes == 0 ? 1 : graph->n_nodes * 2;
struct graph_node *node_arr = realloc(graph->nodes, sizeof(struct graph_node) * new_capacity);
if (!node_arr) {
return -1;
}
graph->size = new_capacity;
graph->nodes = node_arr;
}
struct graph_node *new_node = &graph->nodes[new_id];
new_node->name = strdup(name);
new_node->type = type;
new_node->state = state;
new_node->n_children = 0;
new_node->updated = 1;
new_node->output_values = malloc(type->n_outputs * sizeof(double));
new_node->param_values = malloc(type->n_params * sizeof(double));
new_node->input_srcs = malloc(type->n_inputs * sizeof(struct input_type));
// Check that malloc succeeded in every case which memory was requested
if ((type->n_outputs && !new_node->output_values) ||
(type->n_params && !new_node->param_values) ||
(type->n_inputs && !new_node->input_srcs)) {
return -1;
}
int i;
for (i = 0; i < type->n_inputs; i++) {
new_node->input_srcs[i].controller_id = -1;
}
graph->n_nodes += 1;
// Reset block upon creation
if (new_node->type->reset != NULL) {
new_node->type->reset(new_node->state);
}
return new_id;
}
int graph_set_param_val(struct computation_graph *graph, int node_id, int param_id, double value) {
if (node_id >= graph->n_nodes || param_id >= graph->nodes[node_id].type->n_params) {
return -1;
}
graph->nodes[node_id].param_values[param_id] = value;
graph->nodes[node_id].updated = 1;
return 0;
}
double graph_get_param_val(const struct computation_graph *graph, int node_id, int param_id) {
if (node_id >= graph->n_nodes || param_id >= graph->nodes[node_id].type->n_params) {
return NAN;
}
return graph->nodes[node_id].param_values[param_id];
}
double graph_get_output(const struct computation_graph *graph, int node_id, int output_id) {
if (node_id >= graph->n_nodes || output_id >= graph->nodes[node_id].type->n_outputs) {
return NAN;
}
return graph->nodes[node_id].output_values[output_id];
}
void graph_compute_node_rec(struct computation_graph *graph, int node_id, int depth) {
if (depth >= GRAPH_MAX_DEPTH) {
assert(1 == 0); // TODO :Xil_Assert false
return;
}
if (node_id >= graph->n_nodes) {
return;
}
struct graph_node *node = &graph->nodes[node_id];
if (node->processed_state != UNPROCESSED) {
return;
}
node->processed_state = DISCOVERED;
int input_id;
for (input_id = 0; input_id < node->type->n_inputs; input_id++) {
int src_cntl_id = node->input_srcs[input_id].controller_id;
if (src_cntl_id != -1) {
graph_compute_node_rec(graph, src_cntl_id, depth + 1);
node->updated |= graph->nodes[src_cntl_id].updated;
}
}
if (node->updated) {
// Populate the exec_input_vals array for computation
for (input_id = 0; input_id < node->type->n_inputs; input_id++) {
int src_cntl_id = node->input_srcs[input_id].controller_id;
int src_output_id = node->input_srcs[input_id].controller_output;
if (src_cntl_id != -1) {
exec_input_vals[input_id] = graph->nodes[src_cntl_id].output_values[src_output_id];
}
else {
// Set input value to 0 if not connected
exec_input_vals[input_id] = 0;
}
}
if (node->type->execute != NULL) {
(*node->type->execute)(node->state, node->param_values, exec_input_vals, node->output_values);
}
}
node->processed_state = PROCESSED;
}
void graph_compute_nodes(struct computation_graph *graph, int* node_ids, int n_nodes) {
int i;
for (i = 0; i < graph->n_nodes; i++) {
graph->nodes[i].processed_state = UNPROCESSED;
}
for (i = 0; i < n_nodes; i++) {
int node_id = node_ids[i];
if (node_id < graph->n_nodes) {
graph_compute_node_rec(graph, node_id, 0);
}
}
// Clear all the updated flags for nodes that were actually executed
for (i = 0; i < graph->n_nodes; i++) {
struct graph_node* node = &graph->nodes[i];
if (node->processed_state == PROCESSED) {
node->updated = 0;
}
}
}
int export_dot(const struct computation_graph* graph, FILE* of, int print_outputs) {
fprintf(of, "digraph G {\n"); // Header
fprintf(of, "rankdir=\"LR\"\n"); // Horizontal layout
// Draw all the nodes and their inputs
int i;
for (i = 0; i < graph->n_nodes; i++) {
struct graph_node *node = &graph->nodes[i];
// Create node
fprintf(of, "\"%s\"[shape=record\nlabel=\"", node->name);
fprintf(of, "<f0>%s ", node->name); // Node name is port 0
int j;
// Create ports for inputs
for (j = 0; j < node->type->n_inputs; j++) {
fprintf(of, " |<f%d> --\\>%s", j+1, node->type->input_names[j]);
}
// Create ports for parameters
for (j = 0; j < node->type->n_params; j++) {
fprintf(of, " |<f%d> [%s=%.3f]", j+1+node->type->n_inputs, node->type->param_names[j],node->param_values[j]);
}
fprintf(of, "\"]\n"); // Close label bracket
// Make connections from
for (j = 0; j < node->type->n_inputs; j++) {
int input_id = node->input_srcs[j].controller_id;
if (input_id != -1) {
struct graph_node* src_node = &graph->nodes[input_id];
int output_id = node->input_srcs[j].controller_output;
const char* output_name = src_node->type->output_names[output_id];
fprintf(of, "\"%s\" -> \"%s\":f%d [label=\"%s", src_node->name, node->name, j+1, output_name);
if (print_outputs) {
fprintf(of, "=%.3f", src_node->output_values[output_id]);
}
fprintf(of, "\"]\n");
}
}
}
fprintf(of, "}"); // Close graph
return 0;
}
#ifndef __COMPUTATION_GRAPH_H__
#define __COMPUTATION_GRAPH_H__
#include <stdio.h>
#include <math.h>
typedef void (*execute_node_t)(void *state,
const double* params,
const double *inputs,
double *outputs);
typedef void (*reset_node_t)(void *state);
enum node_processed_state {
UNPROCESSED,
DISCOVERED,
PROCESSED
};
struct computation_graph {
int n_nodes;
int size;
struct graph_node *nodes;
};
// Declares a node type
struct graph_node_type {
const char* const* input_names;
const char* const* output_names;
const char* const* param_names;
int n_inputs;
int n_outputs;
int n_params;
execute_node_t execute;
reset_node_t reset;
};
// Declares an instance of a node
struct graph_node {
const char *name; // Name of this node instance
const struct graph_node_type* type; // Type of this node
double *output_values; // Computed output values
double *param_values; // Values of parameters set for this node
int n_children; // The number of connected children
void *state; // Pointer to the state instance
int processed_state; // State of the node with respecct to the graph traversal
struct input_type { // Array of tuples indicating the source for each input to this node
int controller_id;
int controller_output;
} *input_srcs;
int updated; // 1 if this node has had an input or parameter change
};
/*
* Creates an empty computation graph
* May return NULL on failure
*/
struct computation_graph *create_graph();
/*
* Defines which node's output gets its value passed into the input of a different node.
* Will call reset for each node which was previously orphaned, but is now connected to the graph
* dest_node_id: The ID of the node to which the input belongs
* dest_input: The ID of the input for node dest_cntl to pass the value into
* src_node_id: The node ID where the value is coming from
* src_output: The ID of the output on <src_node_id> where the value comes from
* Returns 0 for success
*/
int graph_set_source(struct computation_graph *graph, int dest_node_id, int dest_input, int src_node_id, int src_output);
/*
* Creates a new node with the given data, and adds it to the graph.
* Returns a negative integer upon failure.
* Otherwise returns a positive integer which is the ID of the new node
*/
int graph_add_node(struct computation_graph *graph,
const char *name,
const struct graph_node_type *type,
void *state);
/*
* Returns the value at the output of the requested node for the requested output.
* Returns 0 if the given node or output IDs are invalid
*/
double graph_get_output(const struct computation_graph *graph, int node_id, int output_id);
/*
* Sets a parameter given by param_id on node node_id to the given value
* Returns 0 upon success
*/
int graph_set_param_val(struct computation_graph *graph, int node_id, int param_id, double value);
/*
* Returns the value of the param at param_id for the given node
* Will return NaN if the given node or parameter IDs are not valid
*/
double graph_get_param_val(const struct computation_graph *graph, int node_id, int param_id);
/*
* Computes the nodes given by node_id.
* To do so, computes all nodes which are ancestors of each given node_id in topological order, with
* the final computation being node_id itself.
*/
void graph_compute_nodes(struct computation_graph *graph, int* node_ids, int n_nodes);
/*
* Writes a graphical representation of the given graph to <of> in the DOT language
*/
int export_dot(const struct computation_graph* graph, FILE* of, int print_outputs);
#endif // __COMPUTATION_GRAPH_H__
#include "node_accumulator.h"
#include <stdlib.h>
struct accum_state {
double accumulated;
};
static void accum_nodes(void *state, const double* params, const double *inputs, double *outputs) {
struct accum_state* my_state = (struct accum_state*)state;
my_state->accumulated += inputs[ACCUM_IN];
outputs[ACCUMULATED] = my_state->accumulated;
}
static void reset(void *state) {
((struct accum_state*)state)->accumulated = 0;
}
static const char* const in_names[2] = {"Accumulator in"};
static const char* const out_names[1] = {"Accumulated"};
static const char* const param_names[0] = {};
const struct graph_node_type node_accum_type = {
.input_names = in_names,
.output_names = out_names,
.param_names = param_names,
.n_inputs = 1,
.n_outputs = 1,
.n_params = 0,
.execute = accum_nodes,
.reset = reset
};
int graph_add_node_accum(struct computation_graph *graph, const char* name) {
struct accum_state* node_state = malloc(sizeof(struct accum_state));
if (sizeof(struct accum_state) && !node_state) {
return -1; // malloc failed
}
return graph_add_node(graph, name, &node_accum_type, node_state);
}
#ifndef __NODE_ACCUMULATOR_H__
#define __NODE_ACCUMULATOR_H__
#include "../computation_graph.h"
int graph_add_node_accum(struct computation_graph *graph, const char* name);
extern const struct graph_node_type node_accum_type;
enum graph_node_accum_inputs {
ACCUM_IN,
};
enum graph_node_accum_outputs {
ACCUMULATED
};
#endif // __NODE_ACCUMULATOR_H__
#include "node_add.h"
#include <stdlib.h>
static void add_nodes(void *state, const double* params, const double *inputs, double *outputs) {
outputs[ADD_SUM] = inputs[ADD_SUMMAND1] + inputs[ADD_SUMMAND2];
}
static void reset(void *state) {}
static const char* const in_names[2] = {"Summand 1", "Summand 2"};
static const char* const out_names[1] = {"Sum"};
static const char* const param_names[0] = {};
const struct graph_node_type node_add_type = {
.input_names = in_names,
.output_names = out_names,
.param_names = param_names,
.n_inputs = 2,
.n_outputs = 1,
.n_params = 0,
.execute = add_nodes,
.reset = reset
};
int graph_add_node_add(struct computation_graph *graph, const char* name) {
return graph_add_node(graph, name, &node_add_type, NULL);
}
#ifndef __NODE_ADD_H__
#define __NODE_ADD_H__
#include "../computation_graph.h"
int graph_add_node_add(struct computation_graph *graph, const char* name);
extern const struct graph_node_type node_add_type;
enum graph_node_add_inputs {
ADD_SUMMAND1,
ADD_SUMMAND2,
};
enum graph_node_add_outputs {
ADD_SUM
};
#endif // __NODE_ADD_H__
#include "node_constant.h"
#include <stdlib.h>
static void output_const(void *state, const double *params, const double *inputs, double *outputs) {
outputs[CONST_VAL] = params[CONST_SET];
}
static void reset(void *state) {}
static const char* const in_names[0] = {};
static const char* const out_names[1] = {"Constant"};
static const char* const param_names[1] = {"Constant"};
const struct graph_node_type node_const_type = {
.input_names = in_names,
.output_names = out_names,
.param_names = param_names,
.n_inputs = 0,
.n_outputs = 1,
.n_params = 1,
.execute = output_const,
.reset = reset
};
int graph_add_node_const(struct computation_graph *graph, const char* name) {
return graph_add_node(graph, name, &node_const_type, NULL);
}
#ifndef __NODE_CONSTANT_H__
#define __NODE_CONSTANT_H__
#include "../computation_graph.h"
int graph_add_node_const(struct computation_graph *graph, const char* name);
extern const struct graph_node_type node_const_type;
enum graph_node_const_params {
CONST_SET
};
enum graph_node_const_outputs {
CONST_VAL
};
#endif //__NODE_CONSTANT_H__
#include "node_gain.h"
#include <stdlib.h>
static void scale_nodes(void *state, const double* params, const double *inputs, double *outputs) {
outputs[GAIN_RESULT] = inputs[GAIN_INPUT] * params[GAIN_GAIN];
}
static void reset(void *state) {}
static const char* const in_names[1] = {"Input"};
static const char* const out_names[1] = {"Amplified"};
static const char* const param_names[1] = {"Gain"};
const struct graph_node_type node_gain_type = {
.input_names = in_names,
.output_names = out_names,
.param_names = param_names,
.n_inputs = 1,
.n_outputs = 1,
.n_params = 1,
.execute = scale_nodes,
.reset = reset
};
int graph_add_node_gain(struct computation_graph *graph, const char* name) {
return graph_add_node(graph, name, &node_gain_type, NULL);
}
#ifndef __NODE_GAIN_H__
#define __NODE_GAIN_H__
#include "../computation_graph.h"
int graph_add_node_gain(struct computation_graph *graph, const char* name);
extern const struct graph_node_type node_gain_type;
enum graph_node_pow_inputs {
GAIN_INPUT
};
enum graph_node_pow_params {
GAIN_GAIN
};
enum graph_node_gain_outputs {
GAIN_RESULT
};
#endif // __NODE_GAIN_H__
#include "node_mult.h"
#include <stdlib.h>
static void mult_nodes(void *state, const double* params, const double *inputs, double *outputs) {
outputs[MULT_PRODUCT] = inputs[MULT_MULTIPLICAND1] * inputs[MULT_MULTIPLICAND2];
}
static void reset(void *state) {}
static const char* const in_names[2] = {"Multiplicand 1", "Multiplicand 2"};
static const char* const out_names[1] = {"Product"};
static const char* const param_names[0] = {};
const struct graph_node_type node_mult_type = {
.input_names = in_names,
.output_names = out_names,
.param_names = param_names,
.n_inputs = 2,
.n_outputs = 1,
.n_params = 0,
.execute = mult_nodes,
.reset = reset
};
int graph_add_node_mult(struct computation_graph *graph, const char* name) {
return graph_add_node(graph, name, &node_mult_type, NULL);
}
#ifndef __NODE_MULT_H__
#define __NODE_MULT_H__
#include "../computation_graph.h"
int graph_add_node_mult(struct computation_graph *graph, const char* name);
extern const struct graph_node_type node_mult_type;
enum graph_node_mult_inputs {
MULT_MULTIPLICAND1,
MULT_MULTIPLICAND2,
};
enum graph_node_mult_outputs {
MULT_PRODUCT
};
#endif // __NODE_MULT_H__
#include "node_pow.h"
#include <stdlib.h>
#include <math.h>
static void pow_nodes(void *state, const double* params, const double *inputs, double *outputs) {
outputs[POW_RESULT] = pow(inputs[POW_BASE], params[POW_EXP]);
}
static void reset(void *state) {}
static const char* const in_names[1] = {"Base"};
static const char* const out_names[1] = {"Result"};
static const char* const param_names[1] = {"Exponent"};
const struct graph_node_type node_pow_type = {
.input_names = in_names,
.output_names = out_names,
.param_names = param_names,
.n_inputs = 1,
.n_outputs = 1,
.n_params = 1,
.execute = pow_nodes,
.reset = reset
};
int graph_add_node_pow(struct computation_graph *graph, const char* name) {
return graph_add_node(graph, name, &node_pow_type, NULL);
}
#ifndef __NODE_POW_H__
#define __NODE_POW_H__
#include "../computation_graph.h"
int graph_add_node_pow(struct computation_graph *graph, const char* name);
extern const struct graph_node_type node_pow_type;
enum graph_node_pow_inputs {
POW_BASE
};
enum graph_node_pow_params {
POW_EXP
};
enum graph_node_pow_outputs {
POW_RESULT
};
#endif // __NODE_POW_H__
#include <stdio.h>
#include "computation_graph.h"
#include "graph_blocks/node_add.h"
#include "graph_blocks/node_mult.h"
#include "graph_blocks/node_constant.h"
#include "graph_blocks/node_gain.h"
#include "tests.h"
int main() {
// struct computation_graph *graph = create_graph();
//
// int const1 = graph_add_node_const(graph, "Const 2");
// graph_set_param_val(graph, const1, CONST_SET, 2);
// int const2 = graph_add_node_const(graph, "Const 1");
// graph_set_param_val(graph, const2, CONST_SET, 3);
//
// int add1_id = graph_add_node_add(graph, "Add");
// graph_set_source(graph, add1_id, ADD_SUMMAND1, const1, CONST_VAL);
// graph_set_source(graph, add1_id, ADD_SUMMAND2, const2, CONST_VAL);
//
// int gain1_id = graph_add_node_gain(graph, "Gain");
// graph_set_param_val(graph, gain1_id, GAIN_GAIN, 3);
// graph_set_source(graph, gain1_id, GAIN_INPUT, add1_id, ADD_SUM);
//
// int mult1_id = graph_add_node_mult(graph, "Mult");
// graph_set_source(graph, mult1_id, MULT_MULTIPLICAND2, gain1_id, GAIN_RESULT);
// graph_set_source(graph, mult1_id, MULT_MULTIPLICAND1, const1, CONST_VAL);
//
// graph_compute_node(graph, mult1_id);
// FILE* dot_fp;
// dot_fp = fopen("..\\comp_graph.dot", "w");
// export_dot(graph, dot_fp);
// fclose(dot_fp);
// printf("Sum is %f\n", graph_get_output(graph, mult1_id, GAIN_RESULT));
int success = graph_run_tests();
printf("Success: %s\n", success == 0 ? "Yes" : "No");
fflush(stdout);
return 0;
}
//
// Created by dawehr on 2/9/2017.
//
#include "tests.h"
#define GRAPH_TEST_EPS 0.00001
static int nequal(double val1, double val2) {
if (fabs(val1 - val2) < GRAPH_TEST_EPS) {
return 0;
}
return -1;
}
int graph_run_tests() {
int success = 0;
success |= graph_test_one_add();
success |= graph_test_circular_runs();
success |= graph_test_circular_resets();
success |= graph_test_accumulator();
success |= graph_test_single_run();
success |= graph_test_reset_rules();
success |= graph_test_self_loop();
return success;
}
int graph_test_one_add() {
struct computation_graph *graph = create_graph();
int block = graph_add_node_add(graph, "Add");
int cblock3 = graph_add_node_const(graph, "3");
graph_set_param_val(graph, cblock3, CONST_SET, 3);
int cblock4 = graph_add_node_const(graph, "4");
graph_set_param_val(graph, cblock4, CONST_SET, 4);
graph_set_source(graph, block, ADD_SUMMAND1, cblock3, CONST_VAL);
graph_set_source(graph, block, ADD_SUMMAND2, cblock4, CONST_VAL);
int to_compute_for[1] = {block};
graph_compute_nodes(graph, to_compute_for, 1);
double result = graph_get_output(graph, block, ADD_SUM);
return nequal(result, 7);
}
int graph_test_circular_runs() {
struct computation_graph *graph = create_graph();
int gain1 = graph_add_node_gain(graph, "gain1");
int gain2 = graph_add_node_gain(graph, "gain2");
graph_set_source(graph, gain2, GAIN_INPUT, gain1, GAIN_RESULT);
graph_set_source(graph, gain1, GAIN_INPUT, gain2, GAIN_RESULT);
int to_compute_for[1] = {gain2};
graph_compute_nodes(graph, to_compute_for, 1);
// If no infinite loop, then success. Value is undefined for circular graphs
return 0;
}
int graph_test_circular_resets() {
struct computation_graph *graph = create_graph();
int acum1 = graph_add_node_accum(graph, "accumulator1");
int acum2 = graph_add_node_accum(graph, "accumulator2");
graph_set_source(graph, acum2, ACCUM_IN, acum1, ACCUMULATED);
graph_set_source(graph, acum1, ACCUM_IN, acum2, ACCUMULATED);
return 0; // Passes if no infinite loop
}
// Tests the accumulator block, thereby testing reset and state changes
int graph_test_accumulator() {
struct computation_graph *graph = create_graph();
int cblock = graph_add_node_const(graph, "const");
int acum_b = graph_add_node_accum(graph, "accumulator");
graph_set_source(graph, acum_b, ACCUM_IN, cblock, CONST_VAL);
int to_compute_for[1] = {acum_b};
graph_set_param_val(graph, cblock, CONST_SET, 3);
graph_compute_nodes(graph, to_compute_for, 1);
graph_set_param_val(graph, cblock, CONST_SET, 8);
graph_compute_nodes(graph, to_compute_for, 1);
graph_set_param_val(graph, cblock, CONST_SET, -2);
graph_compute_nodes(graph, to_compute_for, 1);
double result = graph_get_output(graph, acum_b, ACCUMULATED);
if (nequal(result, 9)) {
printf("graph_test_accumulator failed on step 1, equals %f\n", result);
return -1;
}
// Test reset on source set
int gain_b = graph_add_node_gain(graph, "Gain");
graph_set_param_val(graph, gain_b, GAIN_GAIN, 1);
graph_set_source(graph, gain_b, GAIN_INPUT, acum_b, ACCUMULATED);
to_compute_for[0] = gain_b;
graph_compute_nodes(graph, to_compute_for, 1);
result = graph_get_output(graph, gain_b, GAIN_RESULT);
if (nequal(result, -2)) {
printf("graph_test_accumulator failed on step 2\n");
return -2;
}
return 0;
}
// Tests that a block will only execute once per compute,
// even if its output is connected to multiple inputs
int graph_test_single_run() {
struct computation_graph *graph = create_graph();
int acum_b = graph_add_node_accum(graph, "accumulator");
int add_block = graph_add_node_add(graph, "Add");
int cblock = graph_add_node_const(graph, "const");
graph_set_param_val(graph, cblock, CONST_SET, 2);
graph_set_source(graph, acum_b, ACCUM_IN, cblock, CONST_VAL);
graph_set_source(graph, add_block, ADD_SUMMAND1, acum_b, ACCUMULATED);
graph_set_source(graph, add_block, ADD_SUMMAND2, acum_b, ACCUMULATED);
int to_compute_for[1] = {add_block};
graph_compute_nodes(graph, to_compute_for, 1);
double result = graph_get_output(graph, add_block, ADD_SUM);
return nequal(result, 4);
}
// Tests that upon connection of a second child, a block will not reset
int graph_test_reset_rules() {
struct computation_graph *graph = create_graph();
int cblock = graph_add_node_const(graph, "5");
graph_set_param_val(graph, cblock, CONST_SET, 5);
int acum_b = graph_add_node_accum(graph, "accumulator");
int gain1 = graph_add_node_gain(graph, "gain1");
graph_set_param_val(graph, gain1, GAIN_GAIN, 1);
graph_set_source(graph, gain1, GAIN_INPUT, acum_b, ACCUMULATED);
graph_set_source(graph, acum_b, ACCUM_IN, cblock, CONST_VAL);
int to_compute_for[1] = {gain1};
graph_compute_nodes(graph, to_compute_for, 1);
// state of acum_b is now 5
int gain2 = graph_add_node_gain(graph, "gain2");
graph_set_param_val(graph, gain2, GAIN_GAIN, 1);
// Connect gain 2, and accumulator should not get reset
graph_set_source(graph, gain2, GAIN_INPUT, acum_b, ACCUMULATED);
to_compute_for[0] = gain2;
graph_compute_nodes(graph, to_compute_for, 1);
double result = graph_get_output(graph, gain2, GAIN_RESULT);
return nequal(result, 10);
}
int graph_test_self_loop() {
struct computation_graph *graph = create_graph();
int gain1 = graph_add_node_gain(graph, "gain1");
graph_set_source(graph, gain1, GAIN_INPUT, gain1, GAIN_RESULT);
int to_compute_for[1] = {gain1};
graph_compute_nodes(graph, to_compute_for, 1);
return 0;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment