[Coin-discuss] COIN/Osi bug/portability mods, OsiDylp
Lou Hafer
lou at cs.sfu.ca
Fri Mar 8 11:58:42 EST 2002
Folks,
The OSI layer for dylp is essentially done, modulo some polishing of
makefiles. Along the way, I've turned up a bug or two and a number of portability issues. A list, with explanations, follows. If anyone sees problems
with these (particularly the portability mods), speak up, please.
Lou
==================================
* Common/include/CoinHelperFunctions.hpp:CoinFindDirSeparator
Read from uninitialized error.
The first few lines of code read:
size_t size = 1000;
char* buf;
while (buf != 0) {
buf = new char[size];
Unfortunately, if buf just happens to come up 0, we're screwed. (This happens
in one of the Gnu environments here.) A quick fix is to initialise buf to 1,
as
char* buf = reinterpret_cast<char *>(1) ;
A better fix would be to rewrite the loop. Here's one version, you might want
to try something different.
inline char CoinFindDirSeparator()
{
size_t size = 1000;
char* buf = new char[size] ;
while (buf != 0)
{ if (getcwd(buf, size)) break ;
delete[] buf ;
size = 2*size ;
buf = new char[size] ; }
// if first char is '/' then it's unix and the dirsep is '/'. otherwise we
// assume it's dos and the dirsep is '\'
char dirsep = (buf[0] == '/') ? '/' : '\\' ;
delete[] buf ;
return dirsep ;
}
==================================
* Common/include/CoinSort.hpp:CoinSort_3
Sun Workshop CC compiler template problem + Gnu STL bug.
The function definition
template <class S, class T, class U, class CoinCompare3> void
CoinSort_3(S* sfirst, S* slast, T* tfirst, U* ufirst,
const CoinCompare3& tc)
contains the following line of code:
const int len = std::distance(sfirst, slast);
Unfortunately, the Workshop CC compiler doesn't support the required
template. It's #ifdef'd out with _RWSTD_NO_CLASS_PARTIAL_SPEC. Looking in
stdcomp.h, there's a line of comment that this should be defined `if your
compiler doesn't support partial specialisation of template classes with
default parameters.'
The template in question is
template <class ForwardIterator>
inline _TYPENAME iterator_traits<ForwardIterator>::difference_type
distance (ForwardIterator first, ForwardIterator last)
{
_TYPENAME iterator_traits<ForwardIterator>::difference_type n = 0;
__distance(first, last, n,
iterator_traits<ForwardIterator>::iterator_category());
return n;
}
There's an alternate template which returns the distance through a
reference parameter. If the code is changed to
int len = 0 ;
std::distance(sfirst,slast,len) ;
CC is happy.
The initialisation len = 0 is necessary. When I first tried to build the
COIN unitTest in the Gnu environment, it blew up in
OsiShallowPackedVectorTest, of all places. Once I figured out how to use gdb
well enough to probe, I tracked the problem into CoinSort3, and finally
into std::distance. The two versions in the Gnu library give different
results, and it's obvious from the code. Here's the one that returns the
distance as a return value:
template <class _RandomAccessIterator>
inline typename iterator_traits<_RandomAccessIterator>::difference_type
__distance(_RandomAccessIterator __first, _RandomAccessIterator __last,
random_access_iterator_tag) {
return __last - __first;
}
Now here's the one that returns it through a reference parameter:
template <class _RandomAccessIterator, class _Distance>
inline void __distance(_RandomAccessIterator __first,
_RandomAccessIterator __last,
_Distance& __n, random_access_iterator_tag)
{
__n += __last - __first;
}
Note that `+=' in the second one! If the value coming in isn't zero, you'll
get a very interesting distance. The fix is to initialise len to 0 in
CoinSort_3.
While we're in the vicinity in CoinSort, the call
CoinSort_3(sfirts, slast, tfirst, ufirst, CoinFirstLess_3<S,T,U>());
^^^^^^
is almost certainly wrong. This is around line 380, in the definition of
template <class Iter_S, class Iter_T, class Iter_U> void
CoinSort_3(Iter_S sfirst, Iter_S slast, Iter_T tfirst, Iter_U, ufirst)
for the case where COIN_SORT_ARBITRARY_CONTAINERS is defined.
==========================================
* Osi/include/WarmStartBasis.hpp:setSize, setStructStatus
Coding bugs.
Augmented the function setSize so that it actually sets the size fields,
in addition to allocating the status vectors:
void setSize(int ns, int na) {
delete[] structuralStatus_;
delete[] artificialStatus_;
numStructural_ = ns ;
numArtificial_ = na ;
structuralStatus_ = new char[(ns + 3) / 4];
artificialStatus_ = new char[(na + 3) / 4];
CoinFillN(structuralStatus_, (ns + 3) / 4, (char)0);
CoinFillN(artificialStatus_, (na + 3) / 4, (char)0);
}
Without this modification (or some new functions to set numStructural and
numArtificial), the only way to I could see to set them is with the
constructor that takes sizes and status vectors as parameters. To build a
correct warm start object, you need to create an empty one, setSize it to
the correct size, fill in the status vectors, then construct a second
object using the constructor that takes sizes and vectors, then destroy the
first. Seems overly clumsy. (Alternatively, create the vectors
independently, but that seems to violate all kinds of data-hiding
principles.)
Corrected the mask calculation in the function setStructStatus. It needed the
additional `<<1' to get into the correct position.
void setStructStatus(int i, Status st) {
char& st_byte = structuralStatus_[i>>2];
st_byte &= ~(3 << ((i&3)<<1));
st_byte |= (st << ((i&3)<<1));
}
==========================================
* Osi/include/OsiPackedVectorBase.hpp:
Sun Workshop CC syntax problem.
As I read the C++ standard, the `;' (circa line 145) between two
definitions of isEquivalent should be optional. Gcc doesn't seem to care,
but according to CC, it's a syntax error.
return true;
}; <--- this semicolon
bool isEquivalent(const OsiPackedVectorBase& rhs) const
{
Removing it makes CC much happier.
==========================================
* Osi/include/OsiFloatEqual.hpp:class OsiRelFltEq
Equality and IEEE infinity.
The original code to test equality with scaled tolerance is written as
double tol = fabs(f1) > fabs(f2) ? fabs(f1) : fabs(f2);
tol++;
return fabs(f1-f2) <= epsilon_*tol;
Dylp follows the IEEE floating point standard and uses IEEE infinity. By
definition, inf - inf = NaN (not a number). If you try to test inf == inf
using the original code, the test fails.
Arguably, this is mathematically correct: it's nonsensical to say that inf
== inf. But, under the common trick of approximating infinity with the
largest finite value, inf == inf will come back true and this is what the
OSI test suite expects. Here's a possible fix. Check first for bit-for-bit
equality. inf == inf will be caught here, as will all the other `special'
patterns and any honest numbers which are exactly equal. Once we've done
this, we still need to check that both numbers are finite, which requires
the declaration
extern "C" int finite (double) ;
at file level (or some equivalent). The function actually comes from
libmath, but it's declared in ieeefp.h under Sun, math.h under Gnu.
Then the new code in RelFltEq is
/// Compare function
inline bool operator()(double f1, double f2) const
{
/*
Patch to handle IEEE infinity. To conform with COIN standards, we're
declaring infty = infty. Mathematicians are rolling in their graves.
This test for bit-for-bit equality will succeed for any numbers that are
precisely equal, including the various `special' bit patterns.
*/
if (f1 == f2) return true;
if (!finite(f1) || !finite(f2)) return false;
double tol = fabs(f1) > fabs(f2) ? fabs(f1) : fabs(f2);
tol++;
return fabs(f1-f2) <= epsilon_*tol;
};
==========================================
More information about the Coin-discuss
mailing list