Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
ORNL Quantum Computing Institute
exatn
Commits
51513e15
Commit
51513e15
authored
Jul 25, 2019
by
Mccaskey, Alex
Browse files
Fixing erroneous git push
Signed-off-by:
Alex McCaskey
<
mccaskeyaj@ornl.gov
>
parent
10a01bf8
Changes
35
Hide whitespace changes
Inline
Side-by-side
src/runtime/graph/CMakeLists.txt
View file @
51513e15
set
(
LIBRARY_NAME exatn-runtime-graph
)
file
(
GLOB SRC boost/DirectedBoostGraph.cpp GraphActivator.cpp
)
file
(
GLOB SRC
tensor_exec_state.cpp
boost/directed_boost_graph.cpp
graph_activator.cpp
)
usfunctiongetresourcesource
(
TARGET
${
LIBRARY_NAME
}
OUT SRC
)
usfunctiongeneratebundleinit
(
TARGET
${
LIBRARY_NAME
}
OUT SRC
)
...
...
src/runtime/graph/boost/directed_boost_graph.cpp
0 → 100644
View file @
51513e15
#include
"directed_boost_graph.hpp"
using
namespace
boost
;
namespace
exatn
{
namespace
runtime
{
DirectedBoostGraph
::
DirectedBoostGraph
()
{
dag_
=
std
::
make_shared
<
d_adj_list
>
();
}
VertexIdType
DirectedBoostGraph
::
addOperation
(
std
::
shared_ptr
<
TensorOperation
>
op
)
{
auto
vid
=
add_vertex
(
*
dag_
);
(
*
dag_
)[
vid
].
properties
=
std
::
move
(
std
::
make_shared
<
TensorOpNode
>
(
op
));
(
*
dag_
)[
vid
].
properties
->
setId
(
vid
);
//DAG node id is stored in the node properties
return
vid
;
//new node id in the DAG
}
void
DirectedBoostGraph
::
addDependency
(
VertexIdType
dependent
,
VertexIdType
dependee
)
{
add_edge
(
vertex
(
dependent
,
*
dag_
),
vertex
(
dependee
,
*
dag_
),
*
dag_
);
return
;
}
TensorOpNode
&
DirectedBoostGraph
::
getNodeProperties
(
VertexIdType
vertex_id
)
{
return
*
((
*
dag_
)[
vertex_id
].
properties
);
}
void
DirectedBoostGraph
::
setNodeExecuting
(
VertexIdType
vertex_id
)
{
(
*
dag_
)[
vertex_id
].
properties
->
setExecuting
();
return
;
}
void
DirectedBoostGraph
::
setNodeExecuted
(
VertexIdType
vertex_id
,
int
error_code
)
{
(
*
dag_
)[
vertex_id
].
properties
->
setExecuted
(
error_code
);
return
;
}
bool
DirectedBoostGraph
::
nodeExecuting
(
VertexIdType
vertex_id
)
{
return
(
*
dag_
)[
vertex_id
].
properties
->
isExecuting
();
}
bool
DirectedBoostGraph
::
nodeExecuted
(
VertexIdType
vertex_id
)
{
return
(
*
dag_
)[
vertex_id
].
properties
->
isExecuted
();
}
bool
DirectedBoostGraph
::
dependencyExists
(
VertexIdType
vertex_id1
,
VertexIdType
vertex_id2
)
{
auto
vid1
=
vertex
(
vertex_id1
,
*
dag_
);
auto
vid2
=
vertex
(
vertex_id2
,
*
dag_
);
auto
p
=
edge
(
vid1
,
vid2
,
*
dag_
);
return
p
.
second
;
}
std
::
size_t
DirectedBoostGraph
::
degree
(
VertexIdType
vertex_id
)
{
//return boost::degree(vertex(vertex_id, *dag_), *dag_);
return
getNeighborList
(
vertex_id
).
size
();
}
std
::
size_t
DirectedBoostGraph
::
getNumDependencies
()
{
return
num_edges
(
*
dag_
);
}
std
::
size_t
DirectedBoostGraph
::
getNumNodes
()
{
return
num_vertices
(
*
dag_
);
}
std
::
vector
<
VertexIdType
>
DirectedBoostGraph
::
getNeighborList
(
VertexIdType
vertex_id
)
{
std
::
vector
<
VertexIdType
>
l
;
typedef
typename
boost
::
property_map
<
d_adj_list
,
boost
::
vertex_index_t
>::
type
IndexMap
;
IndexMap
indexMap
=
get
(
boost
::
vertex_index
,
*
dag_
);
typedef
typename
boost
::
graph_traits
<
d_adj_list
>::
adjacency_iterator
adjacency_iterator
;
std
::
pair
<
adjacency_iterator
,
adjacency_iterator
>
neighbors
=
boost
::
adjacent_vertices
(
vertex
(
vertex_id
,
*
dag_
),
*
dag_
);
for
(;
neighbors
.
first
!=
neighbors
.
second
;
++
neighbors
.
first
)
{
VertexIdType
neighborIdx
=
indexMap
[
*
neighbors
.
first
];
l
.
push_back
(
neighborIdx
);
}
return
l
;
}
void
DirectedBoostGraph
::
computeShortestPath
(
VertexIdType
startIndex
,
std
::
vector
<
double
>
&
distances
,
std
::
vector
<
VertexIdType
>
&
paths
)
{
typename
property_map
<
d_adj_list
,
edge_weight_t
>::
type
weightmap
=
get
(
edge_weight
,
*
dag_
);
std
::
vector
<
VertexIdType
>
p
(
num_vertices
(
*
dag_
));
std
::
vector
<
std
::
size_t
>
d
(
num_vertices
(
*
dag_
));
d_vertex_type
s
=
vertex
(
startIndex
,
*
dag_
);
dijkstra_shortest_paths
(
*
dag_
,
s
,
predecessor_map
(
boost
::
make_iterator_property_map
(
p
.
begin
(),
get
(
boost
::
vertex_index
,
*
dag_
)))
.
distance_map
(
boost
::
make_iterator_property_map
(
d
.
begin
(),
get
(
boost
::
vertex_index
,
*
dag_
))));
for
(
const
auto
&
di
:
d
)
distances
.
push_back
(
static_cast
<
double
>
(
di
));
for
(
const
auto
&
pi
:
p
)
paths
.
push_back
(
pi
);
return
;
}
}
// namespace runtime
}
// namespace exatn
src/runtime/graph/boost/directed_boost_graph.hpp
0 → 100644
View file @
51513e15
/** ExaTN:: Tensor Runtime: Directed acyclic graph of tensor operations
REVISION: 2019/07/24
Copyright (C) 2018-2019 Tiffany Mintz, Dmitry Lyakh, Alex McCaskey
Copyright (C) 2018-2019 Oak Ridge National Laboratory (UT-Battelle)
Rationale:
(a) Tensor graph is a directed acyclic graph in which vertices
represent tensor operations and directed edges represent
dependencies between them: A directed edge from node1 to
node2 indicates that node1 depends on node2. Each DAG node
has its unique integer vertex id (VertexIdType) returned
when the node is added to the DAG.
(b) The tensor graph contains:
1. The DAG implementation (in the directed Boost graph subclass);
2. The DAG execution state (TensorExecState data member).
**/
#ifndef EXATN_RUNTIME_DAG_HPP_
#define EXATN_RUNTIME_DAG_HPP_
#include
"tensor_graph.hpp"
#include
"tensor_operation.hpp"
#include
"tensor.hpp"
#include
<boost/graph/adjacency_list.hpp>
#include
<boost/graph/dag_shortest_paths.hpp>
#include
<boost/graph/dijkstra_shortest_paths.hpp>
#include
<boost/graph/eccentricity.hpp>
#include
<boost/graph/exterior_property.hpp>
#include
<boost/graph/floyd_warshall_shortest.hpp>
#include
<boost/graph/graph_traits.hpp>
#include
<boost/graph/graphviz.hpp>
#include
<boost/property_map/property_map.hpp>
#include
<type_traits>
#include
<string>
#include
<memory>
#include
<mutex>
using
namespace
boost
;
namespace
exatn
{
namespace
runtime
{
struct
DirectedBoostVertex
{
std
::
shared_ptr
<
TensorOpNode
>
properties
;
//properties of the DAG node
};
using
d_adj_list
=
adjacency_list
<
vecS
,
vecS
,
directedS
,
DirectedBoostVertex
,
boost
::
property
<
boost
::
edge_weight_t
,
double
>>
;
using
DirectedGraphType
=
std
::
shared_ptr
<
d_adj_list
>
;
using
d_vertex_type
=
typename
boost
::
graph_traits
<
adjacency_list
<
vecS
,
vecS
,
directedS
,
DirectedBoostVertex
,
boost
::
property
<
boost
::
edge_weight_t
,
double
>>>::
vertex_descriptor
;
using
d_edge_type
=
typename
boost
::
graph_traits
<
adjacency_list
<
vecS
,
vecS
,
directedS
,
DirectedBoostVertex
,
boost
::
property
<
boost
::
edge_weight_t
,
double
>>>::
edge_descriptor
;
static_assert
(
std
::
is_same
<
d_vertex_type
,
VertexIdType
>::
value
,
"Vertex id type mismatch!"
);
class
DirectedBoostGraph
:
public
TensorGraph
{
public:
DirectedBoostGraph
();
DirectedBoostGraph
(
const
DirectedBoostGraph
&
)
=
delete
;
DirectedBoostGraph
&
operator
=
(
const
DirectedBoostGraph
&
)
=
delete
;
DirectedBoostGraph
(
DirectedBoostGraph
&&
)
noexcept
=
default
;
DirectedBoostGraph
&
operator
=
(
DirectedBoostGraph
&&
)
noexcept
=
default
;
~
DirectedBoostGraph
()
=
default
;
VertexIdType
addOperation
(
std
::
shared_ptr
<
TensorOperation
>
op
)
override
;
void
addDependency
(
VertexIdType
dependent
,
VertexIdType
dependee
)
override
;
TensorOpNode
&
getNodeProperties
(
VertexIdType
vertex_id
)
override
;
void
setNodeExecuting
(
VertexIdType
vertex_id
)
override
;
void
setNodeExecuted
(
VertexIdType
vertex_id
,
int
error_code
=
0
)
override
;
bool
nodeExecuting
(
VertexIdType
vertex_id
)
override
;
bool
nodeExecuted
(
VertexIdType
vertex_id
)
override
;
bool
dependencyExists
(
VertexIdType
vertex_id1
,
VertexIdType
vertex_id2
)
override
;
std
::
size_t
degree
(
VertexIdType
vertex_id
)
override
;
std
::
size_t
getNumDependencies
()
override
;
std
::
size_t
getNumNodes
()
override
;
std
::
vector
<
VertexIdType
>
getNeighborList
(
VertexIdType
vertex_id
)
override
;
void
computeShortestPath
(
VertexIdType
startIndex
,
std
::
vector
<
double
>
&
distances
,
std
::
vector
<
VertexIdType
>
&
paths
)
override
;
const
std
::
string
name
()
const
override
{
return
"boost-digraph"
;
}
const
std
::
string
description
()
const
override
{
return
"Directed acyclic graph of tensor operations"
;
}
std
::
shared_ptr
<
TensorGraph
>
clone
()
override
{
return
std
::
make_shared
<
DirectedBoostGraph
>
();
}
inline
void
lock
()
{
mtx_
.
lock
();}
inline
void
unlock
()
{
mtx_
.
unlock
();}
protected:
DirectedGraphType
dag_
;
//std::shared_ptr<d_adj_list>
std
::
recursive_mutex
mtx_
;
//object access mutex
};
}
// namespace runtime
}
// namespace exatn
#endif //EXATN_RUNTIME_DAG_HPP_
src/runtime/graph/boost/tests/CMakeLists.txt
View file @
51513e15
exatn_add_test
(
DirectedBoostGraphTester DirectedBoostGraphTester.cpp
)
target_include_directories
(
DirectedBoostGraphTester PRIVATE
${
CMAKE_SOURCE_DIR
}
/src/runtime/graph/boost
${
CMAKE_SOURCE_DIR
}
/src/runtime/graph
${
CMAKE_SOURCE_DIR
}
/src/exatn
${
CMAKE_SOURCE_DIR
}
/tpls/mpark-variant
)
target_link_libraries
(
DirectedBoostGraphTester PRIVATE exatn-runtime-graph Boost::graph
)
target_link_libraries
(
DirectedBoostGraphTester PRIVATE
exatn-numerics
exatn-runtime-graph Boost::graph
)
src/runtime/graph/boost/tests/DirectedBoostGraphTester.cpp
View file @
51513e15
...
...
@@ -11,17 +11,17 @@
* Alexander J. McCaskey - initial API and implementation
*******************************************************************************/
#include
<gtest/gtest.h>
#include
"
D
irected
B
oost
G
raph.hpp"
#include
"
d
irected
_b
oost
_g
raph.hpp"
using
namespace
boost
;
using
namespace
exatn
;
TEST
(
DirectedGraphTester
,
checkConstruction
)
{
// Implement this when we have TensorOp
// Implement this when we have TensorOp
eration
}
int
main
(
int
argc
,
char
**
argv
)
{
::
testing
::
InitGoogleTest
(
&
argc
,
argv
);
return
RUN_ALL_TESTS
();
}
\ No newline at end of file
}
src/runtime/graph/graph_activator.cpp
0 → 100644
View file @
51513e15
#include
"directed_boost_graph.hpp"
#include
"cppmicroservices/BundleActivator.h"
#include
"cppmicroservices/BundleContext.h"
#include
<memory>
#include
<set>
using
namespace
cppmicroservices
;
namespace
{
/**
*/
class
US_ABI_LOCAL
GraphActivator
:
public
BundleActivator
{
public:
GraphActivator
()
{}
/**
*/
void
Start
(
BundleContext
context
)
{
auto
g
=
std
::
make_shared
<
exatn
::
runtime
::
DirectedBoostGraph
>
();
context
.
RegisterService
<
exatn
::
runtime
::
TensorGraph
>
(
g
);
}
/**
*/
void
Stop
(
BundleContext
/*context*/
)
{}
};
}
// namespace
CPPMICROSERVICES_EXPORT_BUNDLE_ACTIVATOR
(
GraphActivator
)
src/runtime/graph/tensor_exec_state.cpp
0 → 100644
View file @
51513e15
/** ExaTN:: Tensor Runtime: Tensor graph execution state
REVISION: 2019/07/23
Copyright (C) 2018-2019 Dmitry Lyakh, Tiffany Mintz, Alex McCaskey
Copyright (C) 2018-2019 Oak Ridge National Laboratory (UT-Battelle)
**/
#include
"tensor_exec_state.hpp"
#include
<iostream>
namespace
exatn
{
namespace
runtime
{
int
TensorExecState
::
incrTensorUpdate
(
const
Tensor
&
tensor
)
{
auto
tens_hash
=
tensor
.
getTensorHash
();
this
->
lock
();
auto
iter
=
tensor_update_cnt_
.
find
(
tens_hash
);
if
(
iter
==
tensor_update_cnt_
.
end
()){
auto
pos
=
tensor_update_cnt_
.
emplace
(
std
::
make_pair
(
tens_hash
,
0
));
iter
=
pos
.
first
;
}
auto
&
count
=
iter
->
second
;
++
count
;
this
->
unlock
();
return
count
;
}
int
TensorExecState
::
decrTensorUpdate
(
const
Tensor
&
tensor
)
{
auto
tens_hash
=
tensor
.
getTensorHash
();
this
->
lock
();
auto
iter
=
tensor_update_cnt_
.
find
(
tens_hash
);
assert
(
iter
!=
tensor_update_cnt_
.
end
());
auto
&
count
=
iter
->
second
;
--
count
;
this
->
unlock
();
return
count
;
}
void
TensorExecState
::
updateLastTensorRead
(
const
Tensor
&
tensor
,
VertexIdType
node_id
)
{
auto
tens_hash
=
tensor
.
getTensorHash
();
this
->
lock
();
auto
iter
=
tensor_last_read_
.
find
(
tens_hash
);
if
(
iter
==
tensor_last_read_
.
end
()){
auto
pos
=
tensor_last_read_
.
emplace
(
std
::
make_pair
(
tens_hash
,
node_id
));
}
else
{
iter
->
second
=
node_id
;
}
this
->
unlock
();
return
;
}
void
TensorExecState
::
updateLastTensorWrite
(
const
Tensor
&
tensor
,
VertexIdType
node_id
)
{
auto
tens_hash
=
tensor
.
getTensorHash
();
this
->
lock
();
auto
iter
=
tensor_last_write_
.
find
(
tens_hash
);
if
(
iter
==
tensor_last_write_
.
end
()){
auto
pos
=
tensor_last_write_
.
emplace
(
std
::
make_pair
(
tens_hash
,
node_id
));
}
else
{
iter
->
second
=
node_id
;
}
this
->
unlock
();
return
;
}
void
TensorExecState
::
clearLastTensorRead
(
const
Tensor
&
tensor
)
{
auto
tens_hash
=
tensor
.
getTensorHash
();
this
->
lock
();
assert
(
tensor_last_read_
.
erase
(
tens_hash
)
==
1
);
this
->
unlock
();
return
;
}
void
TensorExecState
::
clearLastTensorWrite
(
const
Tensor
&
tensor
)
{
auto
tens_hash
=
tensor
.
getTensorHash
();
this
->
lock
();
assert
(
tensor_last_write_
.
erase
(
tens_hash
)
==
1
);
this
->
unlock
();
return
;
}
void
TensorExecState
::
registerDependencyFreeNode
(
VertexIdType
node_id
)
{
this
->
lock
();
nodes_ready_
.
emplace_back
(
node_id
);
this
->
unlock
();
return
;
}
bool
TensorExecState
::
extractDependencyFreeNode
(
VertexIdType
*
node_id
)
{
this
->
lock
();
bool
empty
=
nodes_ready_
.
empty
();
if
(
!
empty
){
*
node_id
=
nodes_ready_
.
front
();
nodes_ready_
.
pop_front
();
}
this
->
unlock
();
return
!
empty
;
}
void
TensorExecState
::
registerExecutingNode
(
VertexIdType
node_id
)
{
this
->
lock
();
nodes_executing_
.
emplace_back
(
node_id
);
this
->
unlock
();
return
;
}
bool
TensorExecState
::
extractExecutingNode
(
VertexIdType
*
node_id
)
{
this
->
lock
();
bool
empty
=
nodes_executing_
.
empty
();
if
(
!
empty
){
*
node_id
=
nodes_executing_
.
front
();
nodes_executing_
.
pop_front
();
}
this
->
unlock
();
return
!
empty
;
}
}
// namespace runtime
}
// namespace exatn
src/runtime/graph/tensor_exec_state.hpp
0 → 100644
View file @
51513e15
/** ExaTN:: Tensor Runtime: Tensor graph execution state
REVISION: 2019/07/23
Copyright (C) 2018-2019 Dmitry Lyakh, Tiffany Mintz, Alex McCaskey
Copyright (C) 2018-2019 Oak Ridge National Laboratory (UT-Battelle)
Rationale:
(a) Tensor graph is a directed acyclic graph in which vertices
represent tensor operations and directed edges represent
dependencies between them: A directed edge from node1 to
node2 indicates that node1 depends on node2. Each DAG node
has its unique integer vertex id (VertexIdType) returned
when the node is added to the DAG.
(b) The tensor graph contains:
1. The DAG implementation (in the DirectedBoostGraph subclass);
2. The DAG execution state (TensorExecState data member).
**/
#ifndef EXATN_RUNTIME_TENSOR_EXEC_STATE_HPP_
#define EXATN_RUNTIME_TENSOR_EXEC_STATE_HPP_
#include
"tensor_operation.hpp"
#include
"tensor.hpp"
#include
<unordered_map>
#include
<list>
#include
<memory>
#include
<mutex>
namespace
exatn
{
namespace
runtime
{
// Tensor Graph node id (DirectedBoostGraph vertex descriptor):
using
VertexIdType
=
std
::
size_t
;
//must match with boost::graph vertex descriptor type
// Tensor implementation:
using
numerics
::
TensorHashType
;
//each numerics::Tensor has its unique integer hash
using
numerics
::
Tensor
;
using
numerics
::
TensorOperation
;
class
TensorExecState
{
public:
TensorExecState
()
=
default
;
TensorExecState
(
const
TensorExecState
&
)
=
delete
;
TensorExecState
&
operator
=
(
const
TensorExecState
&
)
=
delete
;
TensorExecState
(
TensorExecState
&&
)
noexcept
=
default
;
TensorExecState
&
operator
=
(
TensorExecState
&&
)
noexcept
=
default
;
~
TensorExecState
()
=
default
;
/** Registers an update operation on a tensor in the DAG.
Returns the updated outstanding update count on the tensor. **/
int
incrTensorUpdate
(
const
Tensor
&
tensor
);
/** Registers completion of an update operation on a tensor in the DAG.
Returns the updated outstanding update count on the tensor. **/
int
decrTensorUpdate
(
const
Tensor
&
tensor
);
/** Updates the last DAG node id performing a read on a given tensor. **/
void
updateLastTensorRead
(
const
Tensor
&
tensor
,
VertexIdType
node_id
);
/** Updates the last DAG node id performing a write on a given tensor. **/
void
updateLastTensorWrite
(
const
Tensor
&
tensor
,
VertexIdType
node_id
);
/** Clears the last read on a tensor **/
void
clearLastTensorRead
(
const
Tensor
&
tensor
);
/** Clears the last write on a tensor. **/
void
clearLastTensorWrite
(
const
Tensor
&
tensor
);
/** Registers a DAG node without dependencies. **/
void
registerDependencyFreeNode
(
VertexIdType
node_id
);
/** Extracts a dependency-free node from the list.
Returns FALSE if no such node exists. **/
bool
extractDependencyFreeNode
(
VertexIdType
*
node_id
);
/** Registers a DAG node as being executed. **/
void
registerExecutingNode
(
VertexIdType
node_id
);
/** Extracts an executed DAG node from the list. **/
bool
extractExecutingNode
(
VertexIdType
*
node_id
);
inline
void
lock
()
{
mtx_
.
lock
();}
inline
void
unlock
()
{
mtx_
.
unlock
();}
private:
/** Table for tracking the execution status on a given tensor:
Tensor Hash --> Number of outstanding update operations on the Tensor **/
std
::
unordered_map
<
TensorHashType
,
int
>
tensor_update_cnt_
;
/** Table for tracking last read or write access on a given tensor:
Tensor Hash --> Last node of DAG performing read or write on the Tensor **/
std
::
unordered_map
<
TensorHashType
,
VertexIdType
>
tensor_last_read_
;
std
::
unordered_map
<
TensorHashType
,
VertexIdType
>
tensor_last_write_
;
/** List of dependency-free unexecuted DAG nodes **/
std
::
list
<
VertexIdType
>
nodes_ready_
;
/** List of the currently executed DAG nodes **/
std
::
list
<
VertexIdType
>
nodes_executing_
;
/** Object access mutex **/
std
::
recursive_mutex
mtx_
;
};
}
// namespace runtime
}
// namespace exatn
#endif //EXATN_RUNTIME_TENSOR_EXEC_STATE_HPP_
src/runtime/graph/tensor_graph.hpp
0 → 100644
View file @
51513e15
/** ExaTN:: Tensor Runtime: Directed acyclic graph of tensor operations
REVISION: 2019/07/23
Copyright (C) 2018-2019 Tiffany Mintz, Dmitry Lyakh, Alex McCaskey
Copyright (C) 2018-2019 Oak Ridge National Laboratory (UT-Battelle)
Rationale:
(a) Tensor graph is a directed acyclic graph in which vertices
represent tensor operations and directed edges represent
dependencies between them: A directed edge from node1 to
node2 indicates that node1 depends on node2. Each DAG node
has its unique integer vertex id (VertexIdType) returned
when the node is added to the DAG.
(b) The tensor graph contains:
1. The DAG implementation (in the directed Boost graph subclass);
2. The DAG execution state (TensorExecState data member).
**/
#ifndef EXATN_RUNTIME_TENSOR_GRAPH_HPP_
#define EXATN_RUNTIME_TENSOR_GRAPH_HPP_
#include
"Identifiable.hpp"
#include
"tensor_exec_state.hpp"
#include
"tensor_operation.hpp"
#include
"tensor.hpp"
#include
<vector>
#include
<memory>
#include
<mutex>
namespace
exatn
{
namespace
runtime
{
// Tensor Graph node
class
TensorOpNode
{
public:
TensorOpNode
()
:
op_
(
nullptr
),
is_noop_
(
true
),
executing_
(
false
),
executed_
(
false
),
error_
(
0
)
{}
TensorOpNode
(
std
::
shared_ptr
<
TensorOperation
>
tens_op
)
:
op_
(
tens_op
),
is_noop_
(
false
),
executing_
(
false
),
executed_
(
false
),
error_
(
0
)
{}