-
Notifications
You must be signed in to change notification settings - Fork 422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Operation Instances for accessing graph edges during graph construction #360
Conversation
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). For more information, open the CLA check for this pull request. |
signed the CLA let me know what else I have to do this is my first Google contrib. |
Need to rename commit message from odd to add, I'll do this after work. Also maybe this should be a struct with Operation and generated Outputs with names instead of a tuple with HashMap of which I'm not a fan. |
In this case, you can use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does look like a very useful improvement, but I think we could make it a bit better. Rather than returning a map of strings to outputs, it probably makes more sense to return a new type for every op. For example, for the Batch
operation, it would make sense to do:
impl Batch {
pub fn build_outputs<O0: ::std::convert::Into<crate::Output>>(
&self,
in_tensors: O0,
scope: &mut crate::Scope,
) -> crate::Result<BatchOutputs> {
...
}
}
pub struct BatchOutputs {
operation: crate::Operation,
}
impl BatchOutputs {
pub fn operation(&self) -> &crate::Operation {
&self.operation
}
pub fn batched_tensors(&self) -> crate::Output {
crate::Output {
operation: self.operation.clone(),
index: 0,
}
}
pub fn batch_index(&self) -> crate::Output {
crate::Output {
operation: self.operation.clone(),
index: 1,
}
}
pub fn id(&self) -> crate::Output {
crate::Output {
operation: self.operation.clone(),
index: 2,
}
}
}
This means that users would be able to do:
let batch = Batch::new().build_outputs(...)?;
let batched_tensors: Output = batch.batched_tensors();
rather than:
let (batch_op, batch_outputs) = Batch::new().build_outputs(...)?;
let batched_tensors: Output = batch_outputs.get(&"batched_tensors".to_string()).ok_or(some_error())?.clone();
In other words, generating actual methods rather than using a map is much clearer, automatically generates better docs, and means that users don't have to worry about misspelling keys or error handling for missing map entries.
tensorflow-op-codegen/src/main.rs
Outdated
@@ -221,6 +226,98 @@ fn write_attr<W: Write>(w: &mut W, attr: &Attr) -> Result<(), io::Error> { | |||
Ok(()) | |||
} | |||
|
|||
//Corallus-Caninus: same as above but return a HashMap of output names to Outputs so the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to include your name in the comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just wrote something like this let me update the PR.
Edit: my commit is slightly different. It still uses a HashMap but with getters, I can look into a codegen solution but I'm still learning the low level bindgen to the pbtxt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright please tell me any other formatting standards I'll remove the comment names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I can make this happen give me awhile to chew on this.
cee8013
to
f189811
Compare
alright I had some difficulties with git but please let me know what you think of the most recent commit I pushed thanks. |
8c67e95
to
e46b266
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving in the right direction!
src/graph.rs
Outdated
@@ -1059,6 +1069,7 @@ impl Operation { | |||
|
|||
/// Returns the type of a specific input. | |||
pub fn input_type(&self, index: usize) -> DataType { | |||
/// and the return value is the source operation and the index into its output array. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this supposed to go above in the docs for output
?
src/graph.rs
Outdated
Ok(result) | ||
} | ||
} | ||
impl ::core::ops::Index<i64> for OutputSlice { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with the rest of the codebase, this should use std::ops::Index
at the top of the file and then impl Index<i64>
here.
src/graph.rs
Outdated
@@ -1750,6 +1761,98 @@ impl Output { | |||
|
|||
//////////////////////// | |||
|
|||
/// An OutputSlice allows slicing an variable length Operation to retrieve its outputs. | |||
#[derive(Debug, Clone)] | |||
pub struct OutputSlice { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any particular reason to use this rather than Vec<Output>
and a function to create them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope, patching now.
tensorflow-op-codegen/src/main.rs
Outdated
write!(w, "impl {}Outputs {{\n", op_name)?; | ||
|
||
for (i, output) in outputs.iter().enumerate() { | ||
if output.number_attr.is_some() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of calling unwrap()
below, this should do:
if let Some(number_attr) = &output.number_attr {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
patching now.
tensorflow-op-codegen/src/main.rs
Outdated
output.rust_name | ||
)?; | ||
//create an Output for this index | ||
write!(w, " let forward_output = crate::Output {{\n")?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forward_output
doesn't need to be created at all. Instead of forward_output.operation.clone()
below, it can just use self.op.clone()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes thank you I have fixed this.
tensorflow-op-codegen/src/main.rs
Outdated
//now get that Outputs operation and create an OutputSlice given the runtime length | ||
write!( | ||
w, | ||
r#" crate::graph::OutputSlice::new(forward_output.operation.clone(), self.op.get_attr_int("{}").unwrap())"#, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of unwrap()
, this function should return Result<Vec<Output>>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
affirmative.
tensorflow-op-codegen/src/main.rs
Outdated
) -> Result<(), io::Error> { | ||
let mut escaper = Escaper::new(keywords); | ||
let escaped_args: Vec<_> = args.iter().map(|arg| escaper.escape(&arg)).collect(); | ||
write!(w, " fn build_impl_outputs(&self, ")?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be called build_outputs_impl
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so too.
tensorflow-op-codegen/src/main.rs
Outdated
write!(w, " }}\n")?; | ||
for attr in attrs { | ||
write_set_attr(w, attr, &node_var)?; | ||
//TODO: also get type and value of each number_attr by looking up the corresponding attribute |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intended to be done in this PR or later?
tensorflow-op-codegen/src/main.rs
Outdated
//now get that Outputs operation and create an OutputSlice given the runtime length | ||
write!( | ||
w, | ||
r#" crate::graph::OutputSlice::new(forward_output.operation.clone(), self.op.get_attr_int("{}").unwrap())"#, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are always starting at index 0, but that's not correct if this is not the first output arg.
Also, for both this and the else
branch, the index needs to be adjusted for the size of prior outputs. In other words, if the first output_arg
has size 4, and the second has size 5, then the first needs to take outputs 0-3 and the second needs to take 4-8.
To do this, you'll probably need to read all the relevant int attributes as per the TODO above and either pass the Output
s or the indices into the FooOutputs
struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you, patching this now.
tensorflow-op-codegen/src/main.rs
Outdated
number_attr_opt = Some(number_attr.clone()); | ||
} | ||
op_outputs.push(Output { | ||
//NOTE: we should be able to reuse attr keywords, correct me if im wrong. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably use a new Escaper
. Reusing attr_escaper
means that if an op has an attr named foo
and an output named foo
then the generated code would be BlahOutputs::foo_()
(i.e. with an underscore), even though they shouldn't actually clash.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
patching now, Ill also do this for Inputs in the next PR
Thank you for the code review. I will be attending to these errors as well as the following: So looking at the .pbtxt, I think I need to expose inputs too or we will end up doing this eventually. I am thinking about restructuring this PR to create operation specific structs and leaving the builder functions as generic operation types that return these "inherited" operation specific structs for inputs and outputs at least as a starting point. This would also go in another module (eventually) since the code is becoming spaghetti with this PR feature. I've updated this PRs name to reflect this objective. |
d38585f
to
f593359
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I don't know why the format checker refuses to run. Please run cargo fmt
on src/ops/ops_impl.rs
.
Just a few smallish changes, and then I think this will be good to merge.
tensorflow-op-codegen/src/main.rs
Outdated
op_name: &str, | ||
attrs: &[Attr], | ||
args: &[String], | ||
outputs: Vec<Output>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function doesn't use attrs
, args
, or outputs
.
tensorflow-op-codegen/src/main.rs
Outdated
//counts holds the number of times an entry is repeated in offset | ||
let counts = dynamic_offset | ||
.iter() | ||
.fold(HashMap::new(), |mut counts, String| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String
should be lower case, since it's a variable.
tensorflow-op-codegen/src/main.rs
Outdated
" let dynamic_offset = ({}) as i32;\n", | ||
scalar_offsets | ||
)?; | ||
write!(w, " let mut Outputs = vec![];\n",)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outputs
should be lower case since it's a variable.
tensorflow-op-codegen/src/main.rs
Outdated
if dynamic_offset.is_empty() { | ||
write!( | ||
w, | ||
" for i in {}..self.op.get_attr_int(\"{}\")? as i32{{\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of \"{}\"
, it's safer to use {:?}
for these, because it will automatically escape any special characters.
write!(w, "}}\n")?; | ||
|
||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be simplified a bit, but I can hack on it after the PR is merged.
tensorflow-op-codegen/src/main.rs
Outdated
let mut number_attr_opt = None; | ||
if !number_attr.is_empty() { | ||
number_attr_opt = Some(number_attr.clone()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These five lines can be done in one go:
let number_attr_opt = if output.number_attr.is_empty() {
None
} else {
Some(output.number_attr.clone())
};
which also reduces the amount of cloning.
a03c7e0
to
1e9df26
Compare
alright Ive followed up with your review and promised myself I wouldn't add any more features until I open a new PR. Thanks again for your help @adamcrume . |
Thanks! This should be useful for a number of things. |
Expose Output types for each Operation as mentioned in issue #358 and create an object we can add features to such as Input types for runtime (graph build time) access of Operation properties.