Comments (19)
Cool, that would be nice to have. I like it!
Tobias
On Jul 15, 2013, at 5:43 PM, Barry DeZonia [email protected] wrote:
I would like to define an interface in the type hierarchy called FloatingType. Its signature would look like this:
public interface FloatingType(T extends FloatingType(T)) extends ComplexType(T)
It would gather the floating point ops in one place like this:
void sqrt(T result);
void pow(T p, T result);
void log(T result);
void exp(T result);
void sin(T result); // and cos and tan
void sinh(T result) // and cosh and tanh
void asin(T result) // and acos and atan
void asinh(T result) // and acosh and atanhAnd it would be implemented by ComplexDoubleType, ComplexFloatType, FloatType, and DoubleType. The implementations for these methods already exist in OPS so its a minor port.
I haven't investigated this thoroughly but think it makes sense.
—
Reply to this email directly or view it on GitHub.
from imglib2.
Hi Barry,
I agree, we should have that! I just have two questions regarding the implementation:
- why not just: public interface FloatingType( T )? Then any class could implement it ...
- why do method like sqrt, log etc have an argument and not simply work on the instance they are called on, i.e. void sqrt(); void log();
- maybe then could even return themselves so that it can be concatenated, i.e. T sqrt(); T log(); ... so one could do value.log().sqrt() --- I am not very sure on this one though ...
Bye bye,
Steffi
from imglib2.
On Jul 15, 2013, at 7:59 PM, Stephan Preibisch [email protected] wrote:
Hi Barry,
I agree, we should have that! I just have two questions regarding the implementation:
- why not just: public interface FloatingType( T )? Then any class could implement it ...
- why do method like sqrt, log etc have an argument and not simply work on the instance they are called on, i.e. void sqrt(); void log();
Good point. The methods should indeed modify the type in-place to be compatible to the way add() mul() etc. currently behave.
3) maybe then could even return themselves so that it can be concatenated, i.e. T sqrt(); T log(); ... so one ould do value.log().sqrt() --- I am not very sure on this one though ..
Hmm, if we go by compatibility with existing stuff as well, then we should not do that.
Yeah, I agree ... maybe a stupid comment :)
Bye bye,
Steffi—
Reply to this email directly or view it on GitHub.
from imglib2.
Ups, I just edited your comment Tobias, I was not aware that this is possible, or that it makes any sense, sorry. Why can I do that? Nevertheless, I agree on the last point, we would have to do it everywhere then.
from imglib2.
Okay guys I’ve written a (long) reply below. I’m hope this answers your questions satisfactorily. Note I’m not advocating changing any existing api. Just proposing new stuff.
Why FloatingType derives from ComplexType
I am writing a ComplexMath class (similar to Java's Math class) which I'll commit today. Internally it relies on knowing T is complex so that it can use setComplexNumber(), and set/get Real()/Imaginary().
And I’m wondering when would you have a non-numeric FloatingType? Or a non-complex FloatingType for that matter? What does FloatingType mean?
Why to have a T result for the methods
I think that it is useful to separate the calculation of values from the storage of the result of the calculation. You may want to store and use intermediate results.
The current Imglib approach if extended ties you to destroying intermediate values during calculations.
The new approach easily allows y = x + 4 to maintain two variables.
Also with the T result strategy you can still accomplish in place modification if needed.
Example 1:
Show y = x + 4. We want two variable references.
Storage updating strategy
y = x.add(4).copy() // and x’s original value is lost
or
x.add(4) // and no distinguishing an x and a y
or
y = x.copy();
y.add(4)
T result strategy
y = new Blah();
x.add(4, y) // y contains result and x’s original value is intact
Example 2:
Imagine we have a number and we want to figure out it's two adjacent numbers. And we have methods called succ() and pred().
With the storage updating strategy
x = new blah(17)
prev = x.copy().
next = x.copy()
prev.pred();
next.succ();
With T result strategy
x = new blah(17)
prev = new Blah();
next = new Blah();
x.pred(prev);
x.succ(next);
Same amount of code. I think the second version is a bit more elegant. Anytime you want to calc two things from one input this pattern would play out. For instance when one wants to know both the sine and cosine of an angle.
And note that you can also use the T result method to make in place changes:
increment in place: x.succ(x);
decrement in place: x.pred(x);
So you get two approaches satisfied with one signature.
Example 3:
A longer piece of code with more temporaries to show how algorithm looks cleaner
a = 7
b = a + 4
c = a + 8
d = b - 5
With storage updating strategy
a = 7
b = a.copy()
b.add(4)
c = a.copy()
c.add(8)
d = b.copy()
d.sub(5)
With T result strategy
b = new Blah()
c = new Blah()
d = new Blah()
a = 7
a.add(4, b)
a.add(8, c)
b.sub(5, d)
The declaration order of the .copy() methods is important in the first version clouding what is going on with the algorithm.
Why to not return T for operation concatenation
Passing in a T allows the definer of an algorithm to decide how to use memory resources optimally. Returning T’s will eventually have cases where the operator needs to make a new T. This can lead to unnecessary object construction/destruction.
from imglib2.
"What does FloatingType mean?"
Was meant to say "what does FloatingType(String) mean?". Can use greater or less characters in this editor.
from imglib2.
Hi Barry,
I think it should not extend ComplexType, it should only be for T. Simply because there are number definitions with different properties, like quaternions, rgb, or whatever type I want to define complicated math functions on. I should not be forced that my type (remember the game of death for example) has to extend complex. The LifeFormType was a combination of different things, but it could for example do complicated math.
However, there could be a Type that extends it and is complex, no problem with that. Implementation-wise that might have advantages. Above I am only talking about the interface one would demand if this functionality is required.
This makes me think if it actually makes sense to split the interface up even more. Into trigonometric functions, power-functions, log-functions, etc. A very specialized example is that integertype can compute powerOf( T power ) perfectly, but not sqrt().
This again makes me think we should use maybe other names than FloatingType. This does not really say much about its functionality.
Regarding if the result should be an argument, I think you have a point, both (or all three) versions have advantages, but it should be consistent within the type hierarchy. Right now, all operations are performed on the Type they are called on.
Personally, I find it a bit unintuitive that the result is an argument of the function. It can be also easily mistaken without reading the documentation. Is the argument the input or the result? -- x.sin( y ) -- could be either way. x.sin() is clear, although it might require copying the argument. On the plus-side I can compute the sinus of x without a second variable. One could write x.sin( x ), but it also reads weird somehow.
I think it gets especially unclear for functions that require another parameter like x.powerOf( y, z ). y would be the result and z a parameter and x the source ... mmh.
What do you guys think?
Bye bye,
Steffi
from imglib2.
Hi Steffi,
I have been hacking this stuff up the last couple days. The current version is pushed to the floating-type branch of imglib.
In the latest version I have made FloatingType based on T only. It had no consequences in the code.
As for splitting the interfaces further we could do so but then using them becomes more problematic. For instance a class that says MyAlgorithm(T extends NumericType(T) & Exponential(T) & Trig(T) & etc.). I had thought at one point is that we're really interested in the operators and we don't want any function interfaces. That is an approach I took in Types2.java in the types-experiment branch (which is very rough / not done).
And on the topic of passing the result its something you get used to pretty quickly. The result is always the last variable in the statement. Your example: number.power(exponent, result). or angle.sin(result) or num.next(num).
from imglib2.
Hi Barry,
thanks a lot for going ahead implementing it. But I am not convinced we should not do it like that - x.sin( y ), simply because it is inconsistent. Either we change everything, which means all calls including add(), sub(), inc(), and so on or it has to be x.sin(). But a change constitutes a pretty significant API change. I do not have the feeling we thought enough about it to go for it yet.
Regarding the implementation, the synchronized blocks and temporary variables in ComplexMath.java do not seem like a good choice to me. Any multi-threaded code will be slow as any access to temporary types will be synchronized. I would go for an implementation in the respective Type class, by that you also circumvent the problem that the accuracy right now is limited to double. You implemented it like that for the DoubleType and FloatType classes anyways already.
I still feel we might want to split the FloatingType up into smaller interfaces. What if I just need a subset of functionality, or I simply can only implement a subset of it. Also the simple math functions like add, sub etc could be part of it and should not be directly in NumericType.
I am unfortunately very busy writing a proposal right now, I would like to think about it in more detail and also discuss it more, I am sure Herr Saalfeld and Tobias have opinions on that as well. But in general we need that, I totally agree.
Bye bye,
Steffi
from imglib2.
Hi,
with regard to the T result vs in-place modification, I think the most sensible option is to always make the T which you call the method on be the result.
This would be compatible with the existing methods but it is not restricted to in-place.
Barry's example
a = 7
b = a + 4
c = a + 8
d = b - 5would become
a.set(7)
b.add(a, 4)
c.add(a, 8)
d.sub(b, 5)
which is just as clean as the T result.
What do you think?
best regards,
Tobias
On Jul 16, 2013, at 6:05 PM, Barry DeZonia [email protected] wrote:
Why to have a T result for the methods
I think that it is useful to separate the calculation of values from the storage of the result of the calculation. You may want to store and use intermediate results.
The current Imglib approach if extended ties you to destroying intermediate values during calculations.
The new approach easily allows y = x + 4 to maintain two variables.
Also with the T result strategy you can still accomplish in place modification if needed.
Example 1:
Show y = x + 4. We want two variable references.
Storage updating strategy
y = x.add(4).copy() // and x’s original value is lost
or
x.add(4) // and no distinguishing an x and a y
or
y = x.copy();
y.add(4)T result strategy
y = new Blah();
x.add(4, y) // y contains result and x’s original value is intactExample 2:
Imagine we have a number and we want to figure out it's two adjacent numbers. And we have methods called succ() and pred().
With the storage updating strategy
x = new blah(17)
prev = x.copy().
next = x.copy()
prev.pred();
next.succ();With T result strategy
x = new blah(17)
prev = new Blah();
next = new Blah();
x.pred(prev);
x.succ(next);Same amount of code. I think the second version is a bit more elegant. Anytime you want to calc two things from one input this pattern would play out. For instance when one wants to know both the sine and cosine of an angle.
And note that you can also use the T result method to make in place changes:
increment in place: x.succ(x);
decrement in place: x.pred(x);So you get two approaches satisfied with one signature.
Example 3:
A longer piece of code with more temporaries to show how algorithm looks cleaner
a = 7
b = a + 4
c = a + 8
d = b - 5With storage updating strategy
a = 7
b = a.copy()
b.add(4)
c = a.copy()
c.add(8)
d = b.copy()
d.sub(5)With T result strategy
b = new Blah()
c = new Blah()
d = new Blah()
a = 7
a.add(4, b)
a.add(8, c)
b.sub(5, d)The declaration order of the .copy() methods is important in the first version clouding what is going on with the algorithm.
from imglib2.
I like it Tobi. Should I try it out on the branch? Also would we extend this kind of API into NumericType (by addition only, no removing of API)? Or were you just referencing my example?
from imglib2.
I went ahead and made the changes to the FloatingType implementations on the floating-type branch. I did not mess with NumericType.
from imglib2.
Steffi,
I am thinking a little more about the FloatingType(T) extends ComplexType(T) idea. I looked at all the other XType definitions in the hierarchy and all of them extend another type class (like RealType extends ComplexType, ExponentialType extends RealType, NumericType extends Type, etc.). By defining FloatingType outside the hierarchy we are not following convention. Currently the ramification is that if you have an Img(FloatingType) you can do all the sin()'s, cos()'s, tanh()'s you want but you can't add or subtract FloatingTypes.
from imglib2.
Steffi, do you have time to revisit this topic now?
from imglib2.
I would like to get some resolution on this topic. I am leaving the ImageJ project and my last day is November 22, 2013. The code has remained unchanged on the floating-type branch of Imglib2. Can some Imglib developer help move this forward? Thanks.
from imglib2.
Some places where the FloatingType
might be used in the future within ImageJ2:
from imglib2.
I think if we want to tackle these problems we need to introduce an interface hierarchy that also includes simple math (add, sub, mul, div), which is then extended by for example NumericType. This requires serious thinking, but in any way it seems that a proper solution will not be possible with the current type hierarchy that only represents reading, but not writing capabilities.
So the big question is should we develop this half-baked solution? It might still be helpful, but ultimately we need a new Type hierarchy to be able to do this right.
from imglib2.
Last Christmas I mocked up a new type hierarchy here:
It was thrown together in a week or so and may be a little half-baked too. But it supports an algebraically correct number hierarchy. I had thought at the time the next step was to pull the interface methods into interfaces of their own (like Steffi just showed). Anyhow use this if it is of any help.
from imglib2.
@bdezonia A little background: during the hackathon I requested that @StephanPreibisch, @axtimwalde and @tpietzsch look at this branch again, since it was left hanging, and our goal for the hackathon was to bring ImgLib2 out of beta.
While we succeeded in releasing ImgLib2 2.0.0 (and 2.0.1 now), this branch is still left hanging, unfortunately... 😓 I guess the agreed plan was to split up the FloatingType
methods into several different math interfaces, but I'm not sure what the current status is.
@StephanPreibisch (once you are back in the office): any comments/thoughts?
from imglib2.
Related Issues (20)
- Building a kdTree is slow for repeated values HOT 1
- More specific type hierarchy for volatile types
- Remove unnecessary overrides of IterableRealInterval.iterator()
- Spliterators for standard NativeImgs
- Create LocalizableSamplerStream HOT 4
- Explore consequences of adding ImgLibStream wrapper HOT 1
- Stream API documentation
- Improve Point.setPosition
- Consistent toString() implementations
- Deprecate AbstractLocalizingCursor
- Simplify AbstractArrayCursor hierarchy
- Reduce allocation size for Cell objects in CellImg
- Add Util.printCoordinates(Localizable)
- Implement CellLocalizingSpliterator and PlanarLocalizingSpliterator
- Simplify static creation methods
- Views.bundle returns BundleView, but BundleView class is not public HOT 3
- Modularize core ImgLib2 projects HOT 1
- Improve Intervals methods
- WriteConvertedCursor constructor fails with ArrayIndexOutOfBoundsException HOT 6
- Add documentation for SamplerConverter
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from imglib2.