Compare commits
8 Commits
debug-2-15
...
mg_tensorf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d305b66f9 | ||
|
|
04275ce3c0 | ||
|
|
83c5222abd | ||
|
|
7c06b2584f | ||
|
|
9fe8169435 | ||
|
|
bc1d5cf72c | ||
|
|
d19a61c519 | ||
|
|
e5bffeaf67 |
@@ -99,7 +99,8 @@ import_external_library(fmt STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fmt/lib/libfmt.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fmt/include
|
||||
# Skip testing.
|
||||
CMAKE_ARGS -DFMT_TEST=OFF)
|
||||
CMAKE_ARGS -DFMT_TEST=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON)
|
||||
|
||||
|
||||
# Setup ltalloc library
|
||||
@@ -147,7 +148,8 @@ import_external_library(gflags STATIC
|
||||
# Don't register installation in ~/.cmake
|
||||
CMAKE_ARGS -DREGISTER_INSTALL_PREFIX=OFF
|
||||
-DBUILD_gflags_nothreads_LIB=OFF
|
||||
-DGFLAGS_NO_FILENAMES=${GFLAGS_NO_FILENAMES})
|
||||
-DGFLAGS_NO_FILENAMES=${GFLAGS_NO_FILENAMES}
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON)
|
||||
|
||||
# Setup google logging after gflags (so that glog can use it).
|
||||
set(GLOG_DISABLE_OPTIONS "0")
|
||||
|
||||
@@ -10,6 +10,7 @@ add_subdirectory(telemetry)
|
||||
add_subdirectory(communication)
|
||||
add_subdirectory(stats)
|
||||
add_subdirectory(auth)
|
||||
add_subdirectory(tensorflow)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Memgraph Single Node
|
||||
|
||||
@@ -19,5 +19,7 @@ add_library(mg-communication STATIC ${communication_src_files})
|
||||
target_link_libraries(mg-communication Threads::Threads mg-utils mg-io fmt glog gflags)
|
||||
target_link_libraries(mg-communication ${OPENSSL_LIBRARIES})
|
||||
target_include_directories(mg-communication SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
target_link_libraries(mg-communication capnp kj)
|
||||
#target_link_libraries(mg-communication capnp kj)
|
||||
add_dependencies(mg-communication generate_communication_capnp)
|
||||
|
||||
target_compile_options(mg-communication PRIVATE -fPIC)
|
||||
|
||||
@@ -14,3 +14,5 @@ add_library(mg-io STATIC ${io_src_files})
|
||||
target_link_libraries(mg-io stdc++fs Threads::Threads fmt glog mg-utils)
|
||||
target_link_libraries(mg-io capnp kj)
|
||||
add_dependencies(mg-io generate_io_capnp)
|
||||
|
||||
target_compile_options(mg-io PRIVATE -fPIC)
|
||||
|
||||
@@ -24,17 +24,17 @@ Endpoint::Endpoint(const std::string &address, uint16_t port)
|
||||
CHECK(family_ != 0) << "Not a valid IPv4 or IPv6 address: " << address;
|
||||
}
|
||||
|
||||
void Save(const Endpoint &endpoint, capnp::Endpoint::Builder *builder) {
|
||||
builder->setAddress(endpoint.address());
|
||||
builder->setPort(endpoint.port());
|
||||
builder->setFamily(endpoint.family());
|
||||
}
|
||||
// void Save(const Endpoint &endpoint, capnp::Endpoint::Builder *builder) {
|
||||
// builder->setAddress(endpoint.address());
|
||||
// builder->setPort(endpoint.port());
|
||||
// builder->setFamily(endpoint.family());
|
||||
// }
|
||||
|
||||
void Load(Endpoint *endpoint, const capnp::Endpoint::Reader &reader) {
|
||||
endpoint->address_ = reader.getAddress();
|
||||
endpoint->port_ = reader.getPort();
|
||||
endpoint->family_ = reader.getFamily();
|
||||
}
|
||||
// void Load(Endpoint *endpoint, const capnp::Endpoint::Reader &reader) {
|
||||
// endpoint->address_ = reader.getAddress();
|
||||
// endpoint->port_ = reader.getPort();
|
||||
// endpoint->family_ = reader.getFamily();
|
||||
// }
|
||||
|
||||
bool Endpoint::operator==(const Endpoint &other) const {
|
||||
return address_ == other.address_ && port_ == other.port_ &&
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#if 0
|
||||
#include "io/network/endpoint.capnp.h"
|
||||
#endif
|
||||
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
namespace io::network {
|
||||
@@ -33,8 +36,10 @@ class Endpoint {
|
||||
unsigned char family_{0};
|
||||
};
|
||||
|
||||
#if 0
|
||||
void Save(const Endpoint &endpoint, capnp::Endpoint::Builder *builder);
|
||||
|
||||
void Load(Endpoint *endpoint, const capnp::Endpoint::Reader &reader);
|
||||
#endif
|
||||
|
||||
} // namespace io::network
|
||||
|
||||
39
src/tensorflow/CMakeLists.txt
Normal file
39
src/tensorflow/CMakeLists.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
set(communication_src_files
|
||||
${CMAKE_SOURCE_DIR}/src/communication/bolt/v1/value.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/communication/buffer.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/communication/client.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/communication/context.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/communication/helpers.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/communication/init.cpp)
|
||||
|
||||
set(utils_src_files
|
||||
${CMAKE_SOURCE_DIR}/src/utils/demangle.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/utils/file.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/utils/signals.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/utils/thread.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/utils/thread/sync.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/utils/uuid.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/utils/watchdog.cpp)
|
||||
|
||||
set(io_src_files
|
||||
${CMAKE_SOURCE_DIR}/src/io/network/addrinfo.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/io/network/endpoint.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/io/network/socket.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/io/network/utils.cpp)
|
||||
|
||||
#See https://www.tensorflow.org/how_tos/adding_an_op/
|
||||
execute_process(COMMAND python3 -c "import tensorflow; print(tensorflow.sysconfig.get_include())" OUTPUT_VARIABLE Tensorflow_INCLUDE_DIRS)
|
||||
execute_process(COMMAND python3 -c "import tensorflow; print(tensorflow.sysconfig.get_lib() + '/libtensorflow_framework.so', end='')" OUTPUT_VARIABLE Tensorflow_INCLUDE_LIB)
|
||||
|
||||
add_library(memgraph_op SHARED memgraph_op.cc ${communication_src_files} ${utils_src_files} ${io_src_files})
|
||||
target_include_directories(memgraph_op SYSTEM PUBLIC ${Tensorflow_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(memgraph_op ${Tensorflow_INCLUDE_LIB} mg-utils)
|
||||
target_link_libraries(memgraph_op stdc++fs Threads::Threads fmt glog gflags uuid)
|
||||
target_link_libraries(memgraph_op Threads::Threads mg-io fmt glog gflags)
|
||||
target_link_libraries(memgraph_op ${OPENSSL_LIBRARIES})
|
||||
target_include_directories(memgraph_op SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
|
||||
target_compile_definitions(memgraph_op PUBLIC -D_GLIBCXX_USE_CXX11_ABI=0)
|
||||
284
src/tensorflow/memgraph_op.cc
Normal file
284
src/tensorflow/memgraph_op.cc
Normal file
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* \file memgraph_op.cc
|
||||
* \brief Implementation of a memgraph operation in Tensorflow.
|
||||
*/
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <string>
|
||||
|
||||
#include "tensorflow/core/framework/op_kernel.h"
|
||||
#include "tensorflow/core/framework/shape_inference.h"
|
||||
#include "tensorflow/core/framework/tensor_shape.h"
|
||||
#include "tensorflow/core/platform/default/logging.h"
|
||||
|
||||
#include "communication/bolt/client.hpp"
|
||||
#include "communication/bolt/v1/value.hpp"
|
||||
#include "io/network/endpoint.hpp"
|
||||
#include "io/network/utils.hpp"
|
||||
|
||||
using namespace tensorflow;
|
||||
|
||||
const string kHost = "host";
|
||||
const string kPort = "port";
|
||||
const string kUser = "user";
|
||||
const string kPassword = "password";
|
||||
const string kUseSsl = "use_ssl";
|
||||
const string kInputList = "input_list";
|
||||
|
||||
/**
|
||||
* @brief Construct attribute definition.
|
||||
* @details Attribute in TF has special format "key: value = default". This
|
||||
* function is helper function for constructing attribute definition.
|
||||
*
|
||||
* @param key Attribute name
|
||||
* @param value Attribute value
|
||||
* @param default_value Default value
|
||||
* @return "key: value = default_value"
|
||||
*/
|
||||
const string Define(const string& key, const string& value,
|
||||
const string& default_value) {
|
||||
return key + ": " + value + " = " + default_value;
|
||||
}
|
||||
|
||||
REGISTER_OP("MemgraphOp")
|
||||
.Attr(Define(kHost, "string", "'127.0.0.1'"))
|
||||
.Attr(Define(kPort, "int", "7687"))
|
||||
.Attr(Define(kUser, "string", "''"))
|
||||
.Attr(Define(kPassword, "string", "''"))
|
||||
.Attr(Define(kUseSsl, "bool", "false"))
|
||||
.Attr("output_dtype: {int64, double, bool, string}")
|
||||
.Input("query: string")
|
||||
.Input("input_list: int64")
|
||||
.Output("header: string")
|
||||
.Output("rows: output_dtype")
|
||||
.SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) {
|
||||
::tensorflow::shape_inference::ShapeHandle input;
|
||||
TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 0, &input));
|
||||
TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 1, &input));
|
||||
return Status::OK();
|
||||
});
|
||||
|
||||
/**
|
||||
* @brief Memgraph Tensorflow Op
|
||||
* @details Memgraph op is the wrapper around the memgraph client. Memgraph op
|
||||
* takes attributes for connection information and one attribute for output type
|
||||
* definition: int64, double, bool, string. There are two inputs: query (string)
|
||||
* and the input list (int64 list). The user can use the input list in the query
|
||||
* with variable $input_list. There are two outputs: header and rows. Headers
|
||||
* are names of the columns in the output table and rows are all data fetch from
|
||||
* memgraph with the query. Memgraph Op has one limitation on output. All output
|
||||
* values in rows data must have the same type. If the user set output type to
|
||||
* string, then all data convert to the string, however, in any other case
|
||||
* error appears (the implicit cast is not possible for other types). List will
|
||||
* be expand into the matrix. All rows must contains lists with same size.
|
||||
*
|
||||
*
|
||||
* @tparam T Output type
|
||||
*/
|
||||
template <typename T>
|
||||
class MemgraphOp final : public OpKernel {
|
||||
private:
|
||||
const string kBoltClientVersion = "TensorflowMemgraphOp"; // TODO version?
|
||||
string host_;
|
||||
int port_;
|
||||
string user_;
|
||||
string password_;
|
||||
bool use_ssl_;
|
||||
io::network::Endpoint endpoint_;
|
||||
communication::bolt::Client* client_;
|
||||
|
||||
T GetValue(const communication::bolt::Value& value);
|
||||
|
||||
string ToString(const communication::bolt::Value& value) {
|
||||
if (value.IsString())
|
||||
return value.ValueString();
|
||||
else {
|
||||
std::stringstream stream;
|
||||
stream << value;
|
||||
return stream.str();
|
||||
}
|
||||
}
|
||||
|
||||
string ToString(const communication::bolt::Value::Type& type) {
|
||||
std::stringstream stream;
|
||||
stream << type;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @details Instance will connect to memgraph.
|
||||
*
|
||||
* @param context Context contains attribute values - database connection
|
||||
* information and output data type.
|
||||
*/
|
||||
explicit MemgraphOp(OpKernelConstruction* context) : OpKernel(context) {
|
||||
OP_REQUIRES_OK(context, context->GetAttr(kHost, &host_));
|
||||
OP_REQUIRES_OK(context, context->GetAttr(kPort, &port_));
|
||||
OP_REQUIRES_OK(context, context->GetAttr(kUser, &user_));
|
||||
OP_REQUIRES_OK(context, context->GetAttr(kPassword, &password_));
|
||||
OP_REQUIRES_OK(context, context->GetAttr(kUseSsl, &use_ssl_));
|
||||
communication::Init();
|
||||
endpoint_ =
|
||||
io::network::Endpoint(io::network::ResolveHostname(host_), port_);
|
||||
communication::ClientContext context_mg(use_ssl_);
|
||||
client_ = new communication::bolt::Client(&context_mg);
|
||||
OP_REQUIRES(context, client_ != NULL,
|
||||
errors::Internal("Cannot create client instance"));
|
||||
try {
|
||||
client_->Connect(endpoint_, user_, password_, kBoltClientVersion);
|
||||
} catch (const communication::bolt::ClientFatalException& e) {
|
||||
OP_REQUIRES(context, false,
|
||||
errors::Internal(
|
||||
fmt::format("Cannot connect to memgraph: {}", e.what())));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compute stage
|
||||
* @details Op reads input data (query and input list), executes query and on
|
||||
* fills the outputs.
|
||||
*
|
||||
* @param context Context contains data for inputs and outputs.
|
||||
*/
|
||||
void Compute(OpKernelContext* context) override {
|
||||
const Tensor& param_tensor = context->input(1);
|
||||
auto params = param_tensor.flat<int64>();
|
||||
std::vector<communication::bolt::Value> input_list;
|
||||
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
communication::bolt::Value value(static_cast<int64_t>(params(i)));
|
||||
input_list.push_back(value);
|
||||
}
|
||||
|
||||
const Tensor& input_tensor = context->input(0);
|
||||
auto query = input_tensor.flat<string>()(0);
|
||||
|
||||
string message;
|
||||
communication::bolt::QueryData ret;
|
||||
try {
|
||||
ret = client_->Execute(query, {{kInputList, input_list}});
|
||||
} catch (communication::bolt::ClientQueryException& e) {
|
||||
client_->Close();
|
||||
message = fmt::format("Query error: {}", e.what());
|
||||
OP_REQUIRES(context, false, errors::Internal(message));
|
||||
} catch (const communication::bolt::ClientFatalException& e) {
|
||||
client_->Close();
|
||||
message = fmt::format("Internal error: {}", e.what());
|
||||
bool is_connected = false;
|
||||
try {
|
||||
client_->Connect(endpoint_, user_, password_, kBoltClientVersion);
|
||||
ret = client_->Execute(query, {{kInputList, input_list}});
|
||||
is_connected = true;
|
||||
} catch (communication::bolt::ClientQueryException& e) {
|
||||
} catch (const communication::bolt::ClientFatalException& e) {
|
||||
}
|
||||
OP_REQUIRES(context, is_connected, errors::Internal(message));
|
||||
}
|
||||
|
||||
// Use first row to find out Tensor size (inner lists).
|
||||
size_t row_width = 0;
|
||||
size_t row_height = 0;
|
||||
std::vector<size_t> column_list_size(ret.fields.size(), 0);
|
||||
if (ret.records.size() > 0) {
|
||||
for (size_t j = 0, i = 0; j < ret.records[i].size(); ++j) {
|
||||
const auto& field = ret.records[i][j];
|
||||
if (field.IsList())
|
||||
column_list_size[j] = field.ValueList().size();
|
||||
else
|
||||
column_list_size[j] = 1;
|
||||
row_width += column_list_size[j];
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.records.size() == 1 && row_width == 0)
|
||||
row_height = 0;
|
||||
else
|
||||
row_height = ret.records.size();
|
||||
|
||||
// Create header output
|
||||
TensorShape header_output__shape;
|
||||
header_output__shape.AddDim(row_width);
|
||||
Tensor* header_output = NULL;
|
||||
OP_REQUIRES_OK(context, context->allocate_output(0, header_output__shape,
|
||||
&header_output));
|
||||
auto header_output_flat = header_output->flat<string>();
|
||||
|
||||
for (size_t i = 0, cnt = 0; i < ret.fields.size(); ++i)
|
||||
if (column_list_size[i] == 1)
|
||||
header_output_flat(cnt++) = ret.fields[i];
|
||||
else
|
||||
for (size_t j = 0; j < column_list_size[i]; ++j)
|
||||
header_output_flat(cnt++) = ret.fields[i] + "_" + std::to_string(j);
|
||||
|
||||
// Create row output
|
||||
TensorShape rows_output_shape;
|
||||
rows_output_shape.AddDim(row_height);
|
||||
rows_output_shape.AddDim(row_width);
|
||||
Tensor* rows_output = NULL;
|
||||
OP_REQUIRES_OK(
|
||||
context, context->allocate_output(1, rows_output_shape, &rows_output));
|
||||
auto rows_output_matrix = rows_output->matrix<T>();
|
||||
|
||||
for (size_t i = 0; i < row_height; ++i) {
|
||||
for (size_t j = 0, cnt = 0; j < ret.records[i].size(); ++j) {
|
||||
const auto& field = ret.records[i][j];
|
||||
try {
|
||||
if (field.IsList()) {
|
||||
OP_REQUIRES(context,
|
||||
column_list_size[j] == field.ValueList().size(),
|
||||
errors::Internal(fmt::format(
|
||||
"List has wrong size, row: {}, header: {}", i,
|
||||
ret.fields[j])));
|
||||
for (size_t k = 0; k < column_list_size[j]; ++k) {
|
||||
rows_output_matrix(i, cnt++) = GetValue(field.ValueList().at(k));
|
||||
}
|
||||
} else {
|
||||
rows_output_matrix(i, cnt++) = GetValue(field);
|
||||
}
|
||||
} catch (const communication::bolt::ValueException e) {
|
||||
string message =
|
||||
fmt::format("Wrong type: {} = {} ({})", header_output_flat(cnt),
|
||||
field.type(), field);
|
||||
OP_REQUIRES(context, false, errors::Internal(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
int64 MemgraphOp<int64>::GetValue(const communication::bolt::Value& value) {
|
||||
return value.ValueInt();
|
||||
}
|
||||
|
||||
template <>
|
||||
double MemgraphOp<double>::GetValue(const communication::bolt::Value& value) {
|
||||
return value.ValueDouble();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool MemgraphOp<bool>::GetValue(const communication::bolt::Value& value) {
|
||||
return value.ValueBool();
|
||||
}
|
||||
|
||||
template <>
|
||||
string MemgraphOp<string>::GetValue(const communication::bolt::Value& value) {
|
||||
return ToString(value);
|
||||
}
|
||||
|
||||
REGISTER_KERNEL_BUILDER(
|
||||
Name("MemgraphOp").Device(DEVICE_CPU).TypeConstraint<int64>("output_dtype"),
|
||||
MemgraphOp<int64>);
|
||||
REGISTER_KERNEL_BUILDER(Name("MemgraphOp")
|
||||
.Device(DEVICE_CPU)
|
||||
.TypeConstraint<double>("output_dtype"),
|
||||
MemgraphOp<double>);
|
||||
REGISTER_KERNEL_BUILDER(
|
||||
Name("MemgraphOp").Device(DEVICE_CPU).TypeConstraint<bool>("output_dtype"),
|
||||
MemgraphOp<bool>);
|
||||
REGISTER_KERNEL_BUILDER(Name("MemgraphOp")
|
||||
.Device(DEVICE_CPU)
|
||||
.TypeConstraint<string>("output_dtype"),
|
||||
MemgraphOp<string>);
|
||||
46
src/tensorflow/test.py
Executable file
46
src/tensorflow/test.py
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import tensorflow as tf
|
||||
|
||||
# Load libmemgraph_op.so
|
||||
memgraph_op_module = tf.load_op_library('libmemgraph_op.so')
|
||||
|
||||
|
||||
def main():
|
||||
query = """match (u :User)-->(m :Movie)
|
||||
where u.id in $input_list
|
||||
return u.id, m.id;"""
|
||||
|
||||
# Input list used in query
|
||||
input_list = [1, 2, 3, 4, 5]
|
||||
|
||||
# Create tensorflow session
|
||||
with tf.Session() as sess:
|
||||
|
||||
# Query placeholder
|
||||
query_holder = tf.placeholder(tf.string)
|
||||
|
||||
# Input list placeholder
|
||||
input_list_holder = tf.placeholder(tf.int64)
|
||||
|
||||
# Create Memgraph op, and put placeholders for input
|
||||
memgraph_op = memgraph_op_module.memgraph_op(query_holder,
|
||||
input_list_holder,
|
||||
output_dtype=tf.int64)
|
||||
|
||||
# Run Memgraph op
|
||||
output = sess.run(memgraph_op, {query_holder: query,
|
||||
input_list_holder: input_list})
|
||||
|
||||
# First output is list of headers
|
||||
print("Headers:")
|
||||
for i in output[0]:
|
||||
print(i)
|
||||
|
||||
# Output matrix (rows), query results
|
||||
print("Rows: ")
|
||||
for i in output[1]:
|
||||
print(i)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
105
src/tensorflow/test/integration_test.py
Normal file
105
src/tensorflow/test/integration_test.py
Normal file
@@ -0,0 +1,105 @@
|
||||
import unittest
|
||||
import tensorflow as tf
|
||||
import numpy as np
|
||||
|
||||
class MemgrapOp:
|
||||
|
||||
def __init__(self, path_to_lib):
|
||||
self.__memgraph_op_module = tf.load_op_library(path_to_lib)
|
||||
|
||||
def runQuery(self, query, type, input_list=[]):
|
||||
with tf.Session() as sess:
|
||||
query_holder = tf.placeholder(tf.string)
|
||||
input_list_holder = tf.placeholder(tf.int64)
|
||||
memgraph_op = self.__memgraph_op_module.memgraph_op(query_holder, input_list_holder, output_dtype=type)
|
||||
return sess.run(memgraph_op, {query_holder: query, input_list_holder: input_list})
|
||||
|
||||
def runQueryAdvance(self, query, input_list = [], **kargs):
|
||||
with tf.Session() as sess:
|
||||
query_holder = tf.placeholder(tf.string)
|
||||
input_list_holder = tf.placeholder(tf.int64)
|
||||
memgraph_op = self.__memgraph_op_module.memgraph_op(query_holder, input_list_holder, **kargs)
|
||||
return sess.run(memgraph_op, {query_holder: query, input_list_holder: input_list})
|
||||
|
||||
|
||||
|
||||
class MemgraphOpTest(unittest.TestCase):
|
||||
|
||||
def assertRowEqual(self, expected, result):
|
||||
np.testing.assert_array_equal(expected, result)
|
||||
|
||||
def setUp(self):
|
||||
self.memgraph_op = MemgrapOp('../../../build/src/tensorflow/libmemgraph_op.so')
|
||||
|
||||
def test_output_int64(self):
|
||||
header, rows = self.memgraph_op.runQuery("MATCH (n :User) RETURN n.id AS id ORDER BY id LIMIT 5;", tf.int64)
|
||||
self.assertEqual(header, b'id')
|
||||
self.assertRowEqual(rows, [[1],[2],[3],[4],[5]])
|
||||
|
||||
def test_output_double(self):
|
||||
header, rows = self.memgraph_op.runQuery("MATCH (n :User)-[r :Rating]-(:Movie) WHERE r.score < 5 RETURN r.score AS score ORDER BY score DESC LIMIT 5;", tf.double)
|
||||
self.assertEqual(header, b'score')
|
||||
self.assertRowEqual(rows, [[4.5],[4.5],[4.5],[4.5],[4.5]])
|
||||
|
||||
def test_output_bool(self):
|
||||
header, rows = self.memgraph_op.runQuery("MATCH (n :User) RETURN n.name = 'Albert' AS eq ORDER BY n.name LIMIT 5", tf.bool)
|
||||
self.assertEqual(header, b'eq')
|
||||
self.assertRowEqual(rows, [[False], [True], [True], [False], [False]])
|
||||
|
||||
def test_output_string(self):
|
||||
header, rows = self.memgraph_op.runQuery("MATCH (n :User) RETURN n.name AS name ORDER BY name DESC LIMIT 5", tf.string)
|
||||
self.assertEqual(header, b'name')
|
||||
self.assertRowEqual(rows, [[b'Winston'], [b'Winny'], [b'Winnifred'], [b'Winnie'], [b'Winifred']])
|
||||
|
||||
def test_input_list(self):
|
||||
header, rows = self.memgraph_op.runQuery("MATCH (n :User) WHERE n.id IN [25,35,45,55,65] RETURN n.name AS name ORDER BY name ASC", tf.string)
|
||||
self.assertEqual(header, b'name')
|
||||
self.assertRowEqual(rows, [[b'Benny'],[b'Bernard'],[b'Beverly'],[b'Gilbert'],[b'Wilhelmina']])
|
||||
|
||||
def test_matrix_out(self):
|
||||
header, rows = self.memgraph_op.runQuery("MATCH (n :User)-->(m :Movie) RETURN n.id AS NID, m.id AS MID ORDER BY NID, MID LIMIT 10", tf.int64)
|
||||
self.assertRowEqual(header, [b'NID', b'MID'])
|
||||
self.assertRowEqual(rows,[[2,62],[2,110],[2,223],[2,261],[2,319],[2,527],[3,110],[3,527],[4,173],[4,260]])
|
||||
|
||||
def test_connection_host(self):
|
||||
runner = lambda : self.memgraph_op.runQueryAdvance("RETURN 1;", output_dtype=tf.int64, host = "10.0.0.1", port = 1234)
|
||||
with self.assertRaises(Exception) as context:
|
||||
runner()
|
||||
print(str(context.exception))
|
||||
self.assertTrue('Cannot connect to memgraph' in str(context.exception))
|
||||
|
||||
def test_connection_port(self):
|
||||
runner = lambda : self.memgraph_op.runQueryAdvance("RETURN 1;", output_dtype=tf.int64, port = 8888)
|
||||
with self.assertRaises(Exception) as context:
|
||||
runner()
|
||||
self.assertTrue('Cannot connect to memgraph' in str(context.exception))
|
||||
|
||||
def test_connection_ssl(self):
|
||||
runner = lambda : self.memgraph_op.runQueryAdvance("RETURN 1;", output_dtype=tf.int64, use_ssl = True)
|
||||
with self.assertRaises(Exception) as context:
|
||||
runner()
|
||||
print(str(context.exception))
|
||||
self.assertTrue('Cannot connect to memgraph' in str(context.exception))
|
||||
|
||||
def test_wrong_query(self):
|
||||
runner = lambda : self.memgraph_op.runQueryAdvance("SELECT * FROM table", output_dtype=tf.int64)
|
||||
with self.assertRaises(Exception) as context:
|
||||
runner()
|
||||
self.assertTrue('Query error' in str(context.exception))
|
||||
|
||||
def test_wrong_list_size(self):
|
||||
runner = lambda : self.memgraph_op.runQueryAdvance("MATCH (n :User)-->(m :Movie) RETURN n.id, collect(m.id);", output_dtype=tf.int64)
|
||||
with self.assertRaises(Exception) as context:
|
||||
runner()
|
||||
self.assertTrue('List has wrong size' in str(context.exception))
|
||||
|
||||
def test_different_types(self):
|
||||
runner = lambda : self.memgraph_op.runQueryAdvance("MATCH (n :User)-->(m :Movie) RETURN n.id, m.title;", output_dtype=tf.int64)
|
||||
with self.assertRaises(Exception) as context:
|
||||
runner()
|
||||
self.assertTrue('Wrong type' in str(context.exception))
|
||||
|
||||
def test_different_types_string(self):
|
||||
header, rows = self.memgraph_op.runQueryAdvance("MATCH (n :User)-->(m :Movie) RETURN n.id AS NID, m.title AS TITLE ORDER BY NID, TITLE LIMIT 2;", output_dtype=tf.string)
|
||||
self.assertRowEqual(header, [b'NID', b'TITLE'])
|
||||
self.assertRowEqual(rows, [[b'2', b'2001: A Space Odyssey'], [b'2', b'Cat on a Hot Tin Roof']])
|
||||
23
src/tensorflow/test/run_tests.sh
Executable file
23
src/tensorflow/test/run_tests.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
MG_PATH='../../../build/memgraph'
|
||||
MG_CLIENT_PATH='../../../build/tools/src/mg_client'
|
||||
DATA_PATH='../../../tests/qa/tck_engine/tests/movie_example/graphs/movies_graph.cypher'
|
||||
|
||||
# Run Memgraph
|
||||
$MG_PATH &
|
||||
MG_PID=$!
|
||||
disown
|
||||
|
||||
echo "Memgraph running with pid $MG_PID"
|
||||
|
||||
echo "Loading data into Memgraph"
|
||||
sleep 1 # So Memgraph has time to start
|
||||
$MG_CLIENT_PATH --use-ssl=false < $DATA_PATH
|
||||
|
||||
echo "Testing..."
|
||||
python3 -m unittest integration_test.py
|
||||
|
||||
echo "Done."
|
||||
|
||||
kill $MG_PID
|
||||
@@ -9,11 +9,13 @@ set(utils_src_files
|
||||
|
||||
define_add_capnp(add_capnp utils_src_files utils_capnp_files)
|
||||
|
||||
add_capnp(serialization.capnp)
|
||||
#add_capnp(serialization.capnp)
|
||||
|
||||
add_custom_target(generate_utils_capnp DEPENDS ${utils_capnp_files})
|
||||
#add_custom_target(generate_utils_capnp DEPENDS ${utils_capnp_files})
|
||||
|
||||
add_library(mg-utils STATIC ${utils_src_files})
|
||||
target_link_libraries(mg-utils stdc++fs Threads::Threads fmt glog gflags uuid)
|
||||
target_link_libraries(mg-utils capnp kj)
|
||||
add_dependencies(mg-utils generate_utils_capnp)
|
||||
#target_link_libraries(mg-utils capnp kj)
|
||||
#add_dependencies(mg-utils generate_utils_capnp)
|
||||
|
||||
target_compile_options(mg-utils PRIVATE -fPIC)
|
||||
47
src/utils/dector.hpp
Normal file
47
src/utils/dector.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace profiler {
|
||||
|
||||
struct Package {
|
||||
std::string name;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
extern std::vector<Package> profiler_info;
|
||||
|
||||
inline void WriteToFile(const std::string &file_name) {
|
||||
std::ofstream file;
|
||||
file.open(file_name);
|
||||
for (const auto &package : profiler_info) {
|
||||
file << package.name << "\t" << package.size << "\t" << package.capacity
|
||||
<< std::endl;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
template <typename T>
|
||||
class dector : public std::vector<T> {
|
||||
private:
|
||||
std::string file_;
|
||||
int line_;
|
||||
|
||||
public:
|
||||
dector(std::string file, int line) : file_(file), line_(line) {}
|
||||
|
||||
~dector() {
|
||||
std::string vector_name = file_ + ":" + std::to_string(line_);
|
||||
profiler::Package package{vector_name, this->size(), this->capacity()};
|
||||
profiler::profiler_info.push_back(package);
|
||||
}
|
||||
|
||||
void SetName(std::string file, int line) {
|
||||
this->file_ = file;
|
||||
this->line_ = line;
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user