![]() |
|
| | |
| Craig Holman Research Jabberwocky Quotations Contact | |
| Home >
Jabberwocky      |
|
Jabberwocky for June 10, 2008 |
|
The Reduce To Elegance Algorithm    
After not touching my source code for boolean satisfiability testing and reduction for several years, I recently spent several weekends tinkering with it and getting it running again. I'll write up the algorithm properly later, but I want to get it out in some form this week, and that form is some of my current C++ source code for the application. In order to get this source code, I removed a great deal of debugging and tracing code. I've made a good-faith effort to do so with care, but I'm not sure I didn't remove something important. I'll continue to review the code and correct any errors or omissions that I discover. I'm still reviewing and revising the code for reduction to elegance and will post revisions as appropriate.
I'd been planning on posting a proof that the elegance reductions are a complete set of reductions before posting this, but I'm still typing it and am only about halfway through. You'll understand when you see it. The following constraint tree was often used when exploring transformations and may be useful as a reference constraint tree. |
|
|
|
Notes on the source code
A constraint tree is made of of alternating layers of And nodes and Or nodes. The root of a constraint tree is an And node. The Reduce to Elegance algorithm follows this alternating-layer structure in that it has two mutually-recursive methods to handle nodes at each type of layer. The AndReduceToElegance method is applied to And nodes and the OrReduceToElegance method is applied to Or nodes. A CConstraintSet is a class that encompasses two CSets - one a set of positive constraints, and the other a set of negative constraints. A dominant set of a node is the union of the guard sets in the nodes that are in the handle of the broom centered at the node, of the nodes that dominated the node. A side set of a node is the union of the guard sets of the siblings of the node that only contain one constraint.
The InconsistentHandle method tests for an inconsistent handle in the broom centered at the node it is passed and returns the action that should be taken (i.e. keep or delete). The AndCut method tests for an opportunity to remove an unnecessary And node and performs the surgery if warranted. The OrCut method tests for an opportunity to remove an unnecessary Or node and performs the surgery if warranted. The other elegance transformations are tested for and applied in AndReduceToElegance and OrReduceToElegance direct without being packaged as separate methods. The AdoptGreatGrandchildren method takes three parameters, poNode, poDoomedChild, and poDoomedGrandchild, replaces the reference to the doomed child in poNode's list of children with references to poDoomedGrandchild's children, and then deletes poDoomedChild and poDoomedGrandchild. This method is used by both AndCut and OrCut. The ReleaseTree method removes the subtree rooted at the node it is passed, including the node it is passed. The ZeroConstraints method returns true iff the node it is passed has zero children. The OneConstraint method returns true iff the node it is passed has one child. Several of the CSet methods such as Intersect and Union operate on the first two parameters and set the third parameter to the result. _poRoot points to the root node of the constraint tree.
Partial C++ source code for reducing a constraint tree to Elegant Normal Form
The following is also available as a file.
|
EPCResult
CBooleanExpression::
ReduceToElegance()
{
CConstraintSet csetDominant;
CConstraintSet csetSide;
switch ( AndReduceToElegance ( _poRoot, csetDominant, csetSide ) )
{
case keDelete :
ReleaseTree( _poRoot );
_poRoot = NULL;
ReportEvent( keLogicalClass, keContradiction );
break;
case keDisconnect :
ReleaseTree( _poRoot );
_poRoot = NULL;
ReportEvent( keLogicalClass, keTautology );
break;
case keKeep :
if ( _poRoot->_vpoChildren.size() == 0 &&
_poRoot->_csetConstraints.Cardinality() == 0 )
{
ReportEvent( keLogicalClass, keTautology );
}
else
{
ReportEvent( keLogicalClass, keUndetermined );
}
break;
}
return keSuccess;
}
EReductionSignal
CBooleanExpression::
AndReduceToElegance( IN CNode * poCurrentNode,
INOUT CConstraintSet & rcsetDominant,
INOUT CConstraintSet & rcsetSide )
{
CConstraintSet csetHandle;
CConstraintSet csetPrevious;
CConstraintSet csetResult;
///////////////////////////////////////////////////////////////////////////
//
// Subtract redundant constraints
//
///////////////////////////////////////////////////////////////////////////
csetResult = poCurrentNode->_csetConstraints;
poCurrentNode->_csetConstraints -= rcsetDominant;
csetResult -= poCurrentNode->_csetConstraints;
if ( csetResult.Cardinality() > 0 )
{
if ( poCurrentNode->_csetConstraints.Cardinality() == 0 )
{
if ( poCurrentNode->_vpoChildren.size == 0 )
{
return keDisconnect;
}
}
}
///////////////////////////////////////////////////////////////////////////
//
// Remove opposing command constraints
//
///////////////////////////////////////////////////////////////////////////
csetResult = poCurrentNode->_csetConstraints;
poCurrentNode->_csetConstraints._setPositive -= rcsetSide._setNegative;
poCurrentNode->_csetConstraints._setNegative -= rcsetSide._setPositive;
csetResult -= poCurrentNode->_csetConstraints;
///////////////////////////////////////////////////////////////////////////
//
// Detect zero-contraint subsumption by the current node
//
///////////////////////////////////////////////////////////////////////////
if ( poCurrentNode->_vpoChildren.size() == 0 && ZeroConstraints( poCurrentNode ) )
{
return keDisconnect;
}
///////////////////////////////////////////////////////////////////////////
//
// Detect one-constraint subsumption of selections containing the
// current node
//
///////////////////////////////////////////////////////////////////////////
SetIntersection( poCurrentNode->_csetConstraints, rcsetSide, csetResult );
if ( csetResult.Cardinality() > 0 )
{
_bDeleteDueToInconsistentHandle = false;
return keDelete;
}
///////////////////////////////////////////////////////////////////////////
//
// Major cycle of reduction
//
///////////////////////////////////////////////////////////////////////////
do
{
///////////////////////////////////////////////////////////////////////
//
// Remember the current value of the current node's guard set
//
///////////////////////////////////////////////////////////////////////
csetPrevious = poCurrentNode->_csetConstraints;
///////////////////////////////////////////////////////////////////////
//
// Detect an inconsistent handle set
//
///////////////////////////////////////////////////////////////////////
if ( InconsistentHandle( poCurrentNode, rcsetDominant, csetHandle ) == keDelete )
{
return keDelete;
}
///////////////////////////////////////////////////////////////////////
//
// Reduce each child's subtree
//
///////////////////////////////////////////////////////////////////////
int iInitialNumChildren = poCurrentNode->_vpoChildren.size();
for ( std::vector<CNode *>::iterator
ppoChild = poCurrentNode->_vpoChildren.begin();
ppoChild != poCurrentNode->_vpoChildren.end();
)
{
switch ( OrReduceToElegance( *ppoChild, csetHandle, rcsetSide ) )
{
case keDelete :
{
for ( std::vector<CNode *>::iterator
ppoChild2 = poCurrentNode->_vpoChildren.begin();
ppoChild2 != poCurrentNode->_vpoChildren.end();
++ppoChild2 )
{
ReleaseTree( *ppoChild2 );
}
poCurrentNode->_vpoChildren.clear();
return keDelete;
}
case keDisconnect :
ReleaseTree( *ppoChild );
ppoChild = poCurrentNode->_vpoChildren.erase( ppoChild );
if ( poCurrentNode->_vpoChildren.size() == 0 )
{
if ( poCurrentNode->_csetConstraints.Cardinality() == 0 )
{
return keDisconnect;
}
else if ( poCurrentNode->_csetConstraints.Cardinality() == 1 )
{
return keKeepNewTerminalUnit;
}
}
break;
case keKeep :
switch ( AndCut( *ppoChild ) )
{
case keDisconnect :
ReleaseTree( *ppoChild );
ppoChild = poCurrentNode->_vpoChildren.erase( ppoChild );
if ( poCurrentNode->_vpoChildren.size() == 0 )
{
if ( poCurrentNode->_csetConstraints.Cardinality() == 0 )
{
return keDisconnect;
}
else if ( poCurrentNode->_csetConstraints.Cardinality() == 1 )
{
return keKeepNewTerminalUnit;
}
}
break;
case keKeep :
++ppoChild;
break;
case keKeepNewTerminalUnit :
break;
}
break;
case kePromoteConstraints :
poCurrentNode->_csetConstraints.UnionWith( _csetPromotableConstraints );
csetHandle.UnionWith( csetResult );
CConstraintSet csetDoomed;
for ( std::vector<CNode *>::iterator
ppoChild2 = (*ppoChild)->_vpoChildren.begin();
ppoChild2 != (*ppoChild)->_vpoChildren.end();
++ppoChild2 )
{
SetIntersection( (*ppoChild2)->_csetConstraints,
_csetPromotableConstraints, csetDoomed );
(*ppoChild2)->_csetConstraints -= _csetPromotableConstraints;
}
AndCut( *ppoChild );
ppoChild = poCurrentNode->_vpoChildren.begin();
break;
case kePromoteConstraintsAndDisconnect :
poCurrentNode->_csetConstraints.UnionWith( _csetPromotableConstraints );
csetHandle.UnionWith( csetResult );
AndCut( *ppoChild );
ppoChild = poCurrentNode->_vpoChildren.begin();
ReleaseTree( *ppoChild );
ppoChild = poCurrentNode->_vpoChildren.erase( ppoChild );
if ( poCurrentNode->_vpoChildren.size() == 0 )
{
if ( poCurrentNode->_csetConstraints.Cardinality() == 0 )
{
return keDisconnect;
}
else if ( poCurrentNode->_csetConstraints.Cardinality() == 1 )
{
return keKeepNewTerminalUnit;
}
}
break;
}
}
OrCut( poCurrentNode );
}
while ( poCurrentNode->_csetConstraints != csetPrevious );
///////////////////////////////////////////////////////////////////////////
//
// Detect zero-constraint subsumption by the current node
//
///////////////////////////////////////////////////////////////////////////
if ( poCurrentNode->_vpoChildren.size() == 0 &&
ZeroConstraints( poCurrentNode ) )
{
return keDisconnect;
}
return keKeep;
}
EReductionSignal
CBooleanExpression::
OrReduceToElegance( IN CNode * poCurrentNode,
INOUT CConstraintSet & rcsetDominant,
INOUT CConstraintSet & rcsetSide )
{
CConstraintSet csetLocalSide;
CConstraintSet csetResult;
///////////////////////////////////////////////////////////////////////////
//
// Reduce each child's subtree
//
///////////////////////////////////////////////////////////////////////////
for ( std::vector<CNode *>::iterator
ppoChild = poCurrentNode->_vpoChildren.begin();
ppoChild != poCurrentNode->_vpoChildren.end();
)
{
///////////////////////////////////////////////////////////////////////
//
// Construct the local side sets for the current child
//
///////////////////////////////////////////////////////////////////////
csetLocalSide = rcsetSide;
for ( std::vector<CNode *>::iterator
ppoChild2 = poCurrentNode->_vpoChildren.begin();
ppoChild2 != poCurrentNode->_vpoChildren.end();
++ppoChild2 )
{
if ( ppoChild2 != ppoChild && (*ppoChild2)->_vpoChildren.size() == 0 &&
OneConstraint( *ppoChild2 ) )
{
csetLocalSide.UnionWith( (*ppoChild2)->_csetConstraints );
}
}
if ( ! csetLocalSide.IsConsistent() )
{
return keDisconnect;
}
///////////////////////////////////////////////////////////////////////
//
// Reduce the child's subtree
//
///////////////////////////////////////////////////////////////////////
switch ( AndReduceToElegance ( *ppoChild, rcsetDominant, csetLocalSide ) )
{
case keDelete :
if ( poCurrentNode->_vpoChildren.size() > 1 )
{
ReleaseTree( *ppoChild );
ppoChild = poCurrentNode->_vpoChildren.erase( ppoChild );
}
else
{
return keDelete;
}
break;
case keDisconnect :
return keDisconnect;
case keKeep :
++ppoChild;
break;
case keKeepNewTerminalUnit :
ppoChild = poCurrentNode->_vpoChildren.begin();
break;
}
}
///////////////////////////////////////////////////////////////////////////
//
// Detect promotability of constraints
//
///////////////////////////////////////////////////////////////////////////
_csetPromotableConstraints.SetAllElements();
for ( ppoChild = poCurrentNode->_vpoChildren.begin();
ppoChild != poCurrentNode->_vpoChildren.end();
++ppoChild )
{
_csetPromotableConstraints.IntersectWith( (*ppoChild)->_csetConstraints );
}
if ( _csetPromotableConstraints.Cardinality() > 0 )
{
for ( ppoChild = poCurrentNode->_vpoChildren.begin();
ppoChild != poCurrentNode->_vpoChildren.end();
++ppoChild )
{
if ( (*ppoChild)->_csetConstraints == _csetPromotableConstraints )
{
return kePromoteConstraintsAndDisconnect;
}
}
return kePromoteConstraints;
}
else
{
return keKeep;
}
}
void
CBooleanExpression::
AdoptGreatGrandchildren( IN CNode * poNode,
IN CNode * poDoomedChild,
IN CNode * poDoomedGrandchild )
{
std::vector<CNode *> vpoOriginalChildren;
vpoOriginalChildren.insert( vpoOriginalChildren.end(),
poNode->_vpoChildren.begin(),
poNode->_vpoChildren.end() );
poNode->_vpoChildren.clear();
poNode->_vpoChildren.reserve( vpoOriginalChildren.size() - 1 +
poDoomedGrandchild->_vpoChildren.size() );
for ( std::vector<CNode *>::iterator
ppoChild = vpoOriginalChildren.begin();
ppoChild != vpoOriginalChildren.end();
++ppoChild )
{
if ( *ppoChild == poDoomedChild )
{
for ( std::vector<CNode *>::iterator
ppoGreatGrandchild = poDoomedGrandchild->_vpoChildren.begin();
ppoGreatGrandchild != poDoomedGrandchild->_vpoChildren.end();
++ppoGreatGrandchild )
{
poNode->_vpoChildren.push_back( *ppoGreatGrandchild );
}
}
else
{
poNode->_vpoChildren.push_back( *ppoChild );
}
}
poDoomedGrandchild->_vpoChildren.clear();
}
EReductionSignal
CBooleanExpression::
AndCut( IN CNode * poCurrentNode )
{
CNode * poPreviousChild = NULL;
EReductionSignal result = keKeep;
CNode * poTempNode = NULL;
for ( std::vector<CNode *>::iterator
ppoChild = poCurrentNode->_vpoChildren.begin();
ppoChild != poCurrentNode->_vpoChildren.end();
)
{
if ( ZeroConstraints( *ppoChild ) &&
(*ppoChild)->_vpoChildren.size() == 1 )
{
CNode * poDoomedChild = *ppoChild;
ppoChild = poCurrentNode->_vpoChildren.erase( ppoChild );
CNode * poDoomedGrandchild = poDoomedChild->_vpoChildren[0];
for ( std::vector<CNode *>::iterator
ppoGreatGrandchild = poDoomedGrandchild->_vpoChildren.begin();
result != keDisconnect &&
ppoGreatGrandchild != poDoomedGrandchild->_vpoChildren.end();
++ppoGreatGrandchild )
{
if ( (*ppoGreatGrandchild)->_vpoChildren.size() == 0 )
{
switch ( (*ppoGreatGrandchild)->_csetConstraints.Cardinality() )
{
case 0 : result = keDisconnect;
break;
case 1 : result = keKeepNewTerminalUnit;
break;
}
}
}
AdoptGreatGrandchildren( poCurrentNode, poDoomedChild, poDoomedGrandchild );
delete poDoomedGrandchild;
poDoomedGrandchild = NULL;
delete poDoomedChild;
poDoomedChild = NULL;
ppoChild = poCurrentNode->_vpoChildren.begin();
}
else
{
++ppoChild;
}
}
return result;
}
EReductionSignal
CBooleanExpression::
InconsistentHandle( IN CNode * kpoCurrentNode,
INOUT CConstraintSet & rcsetDominant,
INOUT CConstraintSet & rcsetHandle )
{
SetUnion( kpoCurrentNode->_csetConstraints, rcsetDominant, rcsetHandle );
bool bConsistentHandle = rcsetHandle.IsConsistent();
if ( ! bConsistentHandle )
{
_bDeleteDueToInconsistentHandle = true;
}
return bConsistentHandle ? keKeep : keDelete;
}
void
CBooleanExpression::
OrCut( IN CNode * poCurrentNode )
{
for ( std::vector<CNode *>::iterator
ppoChild = poCurrentNode->_vpoChildren.begin();
ppoChild != poCurrentNode->_vpoChildren.end();
)
{
if ( (*ppoChild)->_vpoChildren.size() == 1 )
{
CNode * poDoomedChild = *ppoChild;
CNode * poDoomedGrandchild = poDoomedChild->_vpoChildren[0];
AdoptGreatGrandchildren( poCurrentNode, poDoomedChild, poDoomedGrandchild );
poCurrentNode->_csetConstraints.UnionWith( poDoomedGrandchild->_csetConstraints );
delete poDoomedGrandchild;
poDoomedGrandchild = NULL;
delete poDoomedChild;
poDoomedChild = NULL;
ppoChild = poCurrentNode->_vpoChildren.begin();
}
else
{
++ppoChild;
}
}
}
long
CBooleanExpression::
ReleaseTree( IN CNode * poCurrentNode )
{
long liNumLiteralsReleased = 0;
for ( std::vector<CNode *>::iterator
ppoChild = poCurrentNode->_vpoChildren.begin();
ppoChild != poCurrentNode->_vpoChildren.end();
++ppoChild )
{
liNumLiteralsReleased += ReleaseTree( *ppoChild );
}
poCurrentNode->_vpoChildren.clear();
if ( poCurrentNode->_entType == keAnd )
{
liNumLiteralsReleased += poCurrentNode->_csetConstraints.Cardinality();
}
delete poCurrentNode;
return liNumLiteralsReleased;
}
|
|
     |
| Home >
Jabberwocky      |
|
| Craig Holman Research Jabberwocky Quotations Contact | |
| Patterncraft and Constraint and Consequence are trademarks of Craig Stewart Holman Copyright © 2008 Craig Stewart Holman All Rights Reserved. |
|
| | |
![]() |
|