Boost logo

Boost Users :

Subject: Re: [Boost-users] [boost] [review][constrained_value] Review of ConstrainedValueLibrary begins today
From: jesseperla_at_[hidden]
Date: 2008-12-05 20:19:27


Robert:
As a caveat and analogy: I want to use this to represent mathematics rather
than just bounds-checking. The constrained value manages the subset within
an underlying space (represented by the underlying type int, double, etc.).
Then an instantiation of this is an element in this set. And my goal is to
be able to take an instantiation/element and access information about the
underlying set/type itself (eg managing bounds for optimizers, domain/range
for functors, etc. from only an instance of the type passed in to a generic
function). And if you use linear operators on elements of the subset, then
it always returns members of the underlying space (which is mathematically
correct. integers are a subset of reals which is a linear space with the
+,-,/,* operators on it. Divide an integer by an integer and you get an
element of the superset)

More questions:
1) Storage the same as underlying type?:::::::
> OK, now it makes a bit more sense. Moreover, I believe it is possible
(though
> maybe not trivial) to implement something similar with the current
library.
Cool. This would be necessary for me to use the library, so I hope it is
possible to have dynamic bounds associated with the type. If this was
completed, would the space/storage taken up by an instantiation of the
bounded type be the same as the underlying type? In particular, I would be
interested in having a vector of bounded double's have the same storage aa
vector of bounded doubles (I would be using ublas for example) so that I
could potentially pass on a pointer to a C function for some
interoperability (I know... hacky but sometimes necessary for all the C
scientific computing libraries out there). And for my numerical work,
having an extra parameter or two stored for every value in a big matrix
would eat up cache/memory too quickly for no value.

If necessary to accommodate consistency of the storage with just the
underlying type, perhaps the idea of a bounded value on an instantiation
(like it is today for the runtime) vs. a bounded type (modifiable at
runtime, but common to all instance) could have a different template...
something like:
typedef bounded_type<double, "INTEREST RATE"> interest_rate;
interest_rate::change_bounds(.8, .99);

2) Casting for underlying numeric type?::::::::::
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2199.html
Thanks for educating me on this. I find the myvar.value() syntax to be a
little rough since my functions would want to be able to use both bounded
types and intrinsic types in the same function, but perhaps this could work
with an explicit cast which seems reasonable considering the C++
limitation. For example, would something like the following work to ensure
consistency in my own usage?:
template<typname T>
void myfunc1(T& t)
{
cout << std::max(0.0, static_cast<double>(t)); //Note a floating point here
}
void myfunc2(T& t)
{
cout << std::max(0, static_cast<int>(t)); //Note integer here.
}
typedef bounded_type<double, "INTEREST RATE"> interest_rate;

interest_rate::change_bounds(.8, .99);
interest_rate r = .95;
myfunc1(r); //works since casting internal
myfunc1(.75); //also works, may be a double to double cast...
myfunc2(r); //Would this fail?
myfunc2(1); //Should work

Note on the last one that it is trying to call max with an integer... My
question here is whether this would work? Does the constrained value only
have a direct cast to its underlying type, or is there any way to have it
cast to any types that the underlying type can cast to? I would guess not
for the last case... and this may be where myvar.value() is absolutely
necessary? If the casting to the underlying type always works, then I would
suggest that the myvar.value() is not the right way to go about things.
What we are talking about here really is a cast, like it or not.

3) Bounds including infinity?::::::::
When we are working with bounds, can we use the numeric infinities with
both open and closed sets? I am thinking something like:
typedef bounded_type<double, "Risk Aversion"> risk_aversion;
risk_aversion::change_bounds(1, std::numeric_limits<double>::infinity());
//Might want open or closed depending on if function domain is reals or
extended reals.

4) Numeric limits traits?::::::::::::
For numeric processing, we also need to have some association with the
numeric limit traits. I realize that there is some kind of conceptual
connection between some of the traits here and the bounds themselves, but
for practical purposes, these traits are too useful to ignore. Conceptually
I think of these traits as being information about how the data is stored,
rather than bounds themselves.

What would be really useful is if bounded<> and other types would generate
traits on their own based on the underlying type... this way, we wouldn't
have to create numeric_limits traits for the types ourselves (which is
unreasonable for a library user).

So what I am thinking of is;
typedef bounded_type<double, "Risk Aversion"> risk_aversion;
cout << numeric_limits<risk_aversion>::quiet_NaN();

where the numeric_limits<risk_aversion> subclasses directly from
numeric_limits<double>..... I don't know enough about generic programming
to know how or if this is possible, but this is necessary for generic
numeric programming.

5) Testing set inclusion::::::::
While it is nice to have the bounds checking on the () operator, I would
also want to be able to ask the type if a value is in the set. For example,
in math:
Let Y = integers
Let X = positive integers
y = -1 \in Y
Is y \in X?

In this:
typedef bounded_type<int, "X"> X;
X::change_bounds(0, std::numeric_limits<int>::infinity());
int y = -1;
cout << X::element_of(y); //would return a boolean? what I really would
love to say is is X::element_of() or element_of<X>(y) ?

This is a non-failing operation, and would be used all over in my numeric
code, especially with wrapping optimizers.

6) No debug only functionality please::::::::::::
On all of the conversations about turning off bounds checking on debug, I
would definitely not want this to happen automatically. And a bounds
checking failure should be an exception, not a non-recoverable error. I may
want to use this with an optimizer that tries to put in values out of
bounds, and I would want to catch it and display a message to the console
that the optimization failed, then try with a different initial value.

7) Operations consistent with built in C++ numeric types:::::::::::::::::::
You have successfully educated me on why you need to overload the ++, --,
+, -, etc. operators yourself and can't just revert to the underlying type.
But focusing entirely on intrinsic numeric types double, int, etc.: Will we
have complete coverage of the operators that are defined for these types in
C++? Will the compiler end up generating EXACTLY the same code if I do a
whole bunch of read only operations on a bounded<double> vs. a double? And
will the overloading of these operators work for cross-type operations (eg
multiplying a constrained integer by a constrained double?)? If the answer
is no on these, then we probably can't use this library for scientific
processing.

8) Multi-bounds::::::::::::::::
In comments such as:
http://lists.boost.org/Archives/boost/2008/12/145662.php
I think this is getting away from what I would hope this library can
achieve: Associating sets(in the mathematical sense) of allowed values with
a C++ type. I see what the predicates are used for, but I wouldn't use them
very often. So I don't think we can think of the multi-bounds here being
orthogonal constraints, but rather being a set of disconnected intervals
themselves. Why not orthogonal constraints as described in this link (which
essentially means intersections of subsets)? Try to construct X =
(-infinity, 0] union [1,2] union [3,infinity) with intersecting
intervals....

I would love to have a list of disconnected intervals in the space of the
underlying type that could be parsed through from the . I do not see this
as primarily a predicate question. If this is done with iterators, it might
be even more flexible. For example, say that you only wanted even numbers
and wanted to list out the first 10 intervals of this set... it could
return an iterator to a list of intervals. In this case, it wouldn't
calculate all of the intervals at once, but when you stepped forward, it
would give you the next valid interval (a single value in this case).

Thanks,
Jesse



Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net