Sketcher/GCS: New Block constraint fixes
======================================== Previous versions relied on a heuristic that proved insufficient for cummulative use of the Block constraint. The effect is that Block constraints stopped blocking, which is a major bug, as it lead to inadvertedly moving geometry that was supposed to be blocked. Fixes: https://forum.freecadweb.org/viewtopic.php?f=13&t=53515&start=30#p461215 (Thanks Chaospilot) Know problems with old block constraint (v0.18): 1. If driving constraints were present, they were ignored if inserted before the block constraint (to avoid redundancy/conflicting). They resulted in Principles of Working of the new block constraint: 1. Handling of the new block constraint is based two processes, a pre-analysis and a post-analysis. Pre-analysis works *before* diagnosing the system in the solver. Post-analysis works *after* diagnosing the system in the solver. 2. Pre-analysis is directed to detect geometries affected *exclusively* by a block constraint. This is important because these geometries can be pre-fixed when creating the solver geometry and constraints before GCS::diagnose() via initSolution() AND because if no other constraint affects the geometry, the geometry parameters won't even appear in the Jacobian of GCS, so they won't be reported as dependent parameters (for which post-analysis would be of no use). 3. Post-analysis is directed to detect Geometries affected *not only* by a block constraint. This is important because pre-fixing these geometries would lead to redundant constraints. The post-analysis, enables to fix just the parameters that fulfil the dependacy groups. 4. Post-analysis basically identifies which parameters shall be fixed to make geometries having blocking constraints fixed, while not leading to redundant/conflicting constraints. These parameters must belong to blocked geometry. This is, groups may comprise parameters belonging to blocked geometry and parameters belonging to unconstrained geometry. It is licit that the latter remain as dependent parameters. The former are referred to as "blockable parameters". Extending this concept, there may be unsatisfiable groups (because they do not comprise any bloackable parameter), and it is the desired outcome NOT to satisfy such groups. It must be emphasised that there is not a single combination of fixed parameters from the blockable parameters that satisfy all the dependency groups. However: 1) some combinations do not satisfy all the dependency groups that must be satisfied (e.g. fixing one group containing two blockable parameters with a given one may result in another group, fixable only by the former, not to be satisfied). This leads, in a subsequent diagnosis, to satisfiable unsatisfied groups. 2) some combinations lead to partially redundant constraints, that the solver will silently drop in a subsequent diagnosis, thereby reducing the rank of the system fixing less than it should. 5. The implementation rationale is as follows: 1) The implementation is on the order of the groups provided by the QR decomposition used to reveal the parameters (see System::identifyDependentParameters in GCS). Zeros are made over the pilot of the full R matrix of the QR decomposition, which is a top triangular matrix.This, together with the permutation matrix, allow to know groups of dependent parameters (cols between rank and full size). Each group refers to a new parameter not affected by the rank in combination with other free parameters intervening in the rank (because of the triangular shape of the R matrix). This results in that each the first column between the rank and the full size, may only depend on a number of parameters, while the last full size colum may dependent on any amount of previously introduced parameters. 2) Thus the rationale is to start from the last group (having **potentially** the larger amount of parameters) and selecting as blocking for that group the latest blockable parameter. Because previous groups do not have access to the last parameter, this can never interfere with previous groups. However, because the last parameter may not be a blockable one, there is a risk of selecting a parameter common with other group, albeit the probability is reduced and probably (I have not demonstrated it though and I am not sure), it systematically leads to the right solution in one iteration. GCS: Change dependency group from std::set to std::vector to prevent reordering of parameters.
This commit is contained in:
committed by
abdullahtahiriyo
parent
e20a21a97d
commit
c0e06f7f19
@@ -130,6 +130,85 @@ void Sketch::clear(void)
|
||||
malformedConstraints = false;
|
||||
}
|
||||
|
||||
bool Sketch::analyseBlockedGeometry( const std::vector<Part::Geometry *> &internalGeoList,
|
||||
const std::vector<Constraint *> &constraintList,
|
||||
std::vector<bool> &onlyblockedGeometry,
|
||||
std::vector<int> &blockedGeoIds) const
|
||||
{
|
||||
bool isSomethingBlocked = false;
|
||||
bool doesBlockAffectOtherConstraints = false;
|
||||
|
||||
int geoindex = 0;
|
||||
for(auto g : internalGeoList) {
|
||||
if(GeometryFacade::getBlocked(g)) {
|
||||
// is it only affected by one constraint, the block constraint (and this is driving), or by any other driving constraint ?
|
||||
bool blockOnly = true;
|
||||
bool blockisDriving = false;
|
||||
|
||||
for(auto c : constraintList) {
|
||||
// is block driving
|
||||
if( c->Type == Sketcher::Block && c->isDriving && c->First == geoindex)
|
||||
blockisDriving = true;
|
||||
|
||||
if( c->Type != Sketcher::Block && c->isDriving &&
|
||||
(c->First == geoindex || c->Second == geoindex || c->Third == geoindex) )
|
||||
blockOnly = false;
|
||||
}
|
||||
|
||||
if(blockisDriving) {
|
||||
if(blockOnly) {
|
||||
onlyblockedGeometry[geoindex] = true; // we pre-fix this geometry
|
||||
isSomethingBlocked = true;
|
||||
}
|
||||
else {
|
||||
// we will have to pos-analyse the first diagnose result for these geometries
|
||||
// in order to avoid redundant constraints
|
||||
isSomethingBlocked = true;
|
||||
doesBlockAffectOtherConstraints = true;
|
||||
blockedGeoIds.push_back(geoindex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
geoindex++;
|
||||
}
|
||||
|
||||
if(isSomethingBlocked) {
|
||||
|
||||
// look for internal geometry linked IAs
|
||||
for(auto c : constraintList) {
|
||||
if(c->Type == InternalAlignment) {
|
||||
|
||||
auto geoit = std::find(blockedGeoIds.begin(),blockedGeoIds.end(),c->Second);
|
||||
|
||||
if(geoit != blockedGeoIds.end() || onlyblockedGeometry[c->Second]) { // internal alignment geometry found, add to list
|
||||
// check if pre-fix or post-analyses
|
||||
bool blockAffectedOnly = true;
|
||||
|
||||
for(auto ic : constraintList) {
|
||||
// there is another driving constraint
|
||||
if( ic->Type != Sketcher::Block && ic->isDriving &&
|
||||
(ic->First == c->First || ic->Second == c->First || ic->Third == c->First))
|
||||
blockAffectedOnly = false;
|
||||
}
|
||||
|
||||
if(blockAffectedOnly) {
|
||||
onlyblockedGeometry[c->Second] = true; // we pre-fix this geometry
|
||||
}
|
||||
else {
|
||||
// we will have to post-analyse the first diagnose result for these geometries
|
||||
// in order to avoid redundant constraints
|
||||
doesBlockAffectOtherConstraints = true;
|
||||
blockedGeoIds.push_back(*geoit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return doesBlockAffectOtherConstraints;
|
||||
}
|
||||
|
||||
int Sketch::setUpSketch(const std::vector<Part::Geometry *> &GeoList,
|
||||
const std::vector<Constraint *> &ConstraintList,
|
||||
int extGeoCount)
|
||||
@@ -144,13 +223,43 @@ int Sketch::setUpSketch(const std::vector<Part::Geometry *> &GeoList,
|
||||
for (int i=int(GeoList.size())-extGeoCount; i < int(GeoList.size()); i++)
|
||||
extGeoList.push_back(GeoList[i]);
|
||||
|
||||
std::vector<bool> blockedGeometry(intGeoList.size(),false); // these geometries are blocked, frozen and sent as fixed parameters to the solver
|
||||
std::vector<bool> onlyBlockedGeometry(intGeoList.size(),false); // these geometries are blocked, frozen and sent as fixed parameters to the solver
|
||||
std::vector<bool> unenforceableConstraints(ConstraintList.size(),false); // these constraints are unenforceable due to a Blocked constraint
|
||||
|
||||
/* This implements the old block constraint. I have decided not to remove it at this time while the new is tested, just in case the change
|
||||
* needs to be reverted */
|
||||
/*if(!intGeoList.empty())
|
||||
getBlockedGeometry(blockedGeometry, unenforceableConstraints, ConstraintList);*/
|
||||
|
||||
addGeometry(intGeoList,blockedGeometry);
|
||||
// Pre-analysis of blocked geometry (new block constraint) to fix geometry only affected by a block constraint (see comment in Sketch.h)
|
||||
std::vector<int> blockedGeoIds;
|
||||
bool doesBlockAffectOtherConstraints = analyseBlockedGeometry( intGeoList,
|
||||
ConstraintList,
|
||||
onlyBlockedGeometry,
|
||||
blockedGeoIds);
|
||||
|
||||
#ifdef DEBUG_BLOCK_CONSTRAINT
|
||||
if(doesBlockAffectOtherConstraints)
|
||||
Base::Console().Log("\n Block interferes with other constraints: Post-analysis required");
|
||||
|
||||
Base::Console().Log("\nOnlyBlocked GeoIds:");
|
||||
size_t i = 0;
|
||||
for(; i < onlyBlockedGeometry.size(); i++) {
|
||||
if(onlyBlockedGeometry[i])
|
||||
Base::Console().Log("\n GeoId=%d", i);
|
||||
}
|
||||
if( i == 0)
|
||||
Base::Console().Log("\n None");
|
||||
|
||||
Base::Console().Log("\nNotOnlyBlocked GeoIds:");
|
||||
i = 0;
|
||||
for(; i < blockedGeoIds.size(); i++)
|
||||
Base::Console().Log("\n GeoId=%d", blockedGeoIds[i]);
|
||||
if( i == 0)
|
||||
Base::Console().Log("\n None");
|
||||
#endif //DEBUG_BLOCK_CONSTRAINT
|
||||
|
||||
addGeometry(intGeoList,onlyBlockedGeometry);
|
||||
int extStart=Geoms.size();
|
||||
addGeometry(extGeoList, true);
|
||||
int extEnd=Geoms.size()-1;
|
||||
@@ -165,158 +274,43 @@ int Sketch::setUpSketch(const std::vector<Part::Geometry *> &GeoList,
|
||||
GCSsys.declareUnknowns(Parameters);
|
||||
GCSsys.declareDrivenParams(DrivenParameters);
|
||||
GCSsys.initSolution(defaultSolverRedundant);
|
||||
GCSsys.getConflicting(Conflicting);
|
||||
GCSsys.getRedundant(Redundant);
|
||||
GCSsys.getDependentParams(pDependentParametersList);
|
||||
|
||||
calculateDependentParametersElements();
|
||||
// Post-analysis
|
||||
// Now that we have all the parameters information, we deal properly with the block constraints if necessary
|
||||
if(doesBlockAffectOtherConstraints) {
|
||||
|
||||
// Now that we have all the parameters information, we deal properly with the block constraint
|
||||
|
||||
bool isSomethingBlocked = false;
|
||||
std::vector<int> blockedGeoIds;
|
||||
|
||||
int geoindex = 0;
|
||||
for(auto & g : Geoms) {
|
||||
if(!g.external && GeometryFacade::getBlocked(g.geo)) {
|
||||
isSomethingBlocked = true;
|
||||
blockedGeoIds.push_back(geoindex);
|
||||
}
|
||||
geoindex++;
|
||||
}
|
||||
|
||||
if(isSomethingBlocked) {
|
||||
|
||||
// 0. look for internal geometry linked IAs
|
||||
for(auto c : ConstraintList) {
|
||||
if(c->Type == InternalAlignment) {
|
||||
|
||||
auto geoit = std::find(blockedGeoIds.begin(),blockedGeoIds.end(),c->Second);
|
||||
|
||||
if(geoit != blockedGeoIds.end()) { // internal alignment geometry found, add to list
|
||||
blockedGeoIds.push_back(*geoit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 1. Look what needs blocking
|
||||
std::vector<double *> params_to_block;
|
||||
std::vector < std::set < double*>> groups;
|
||||
GCSsys.getDependentParamsGroups(groups);
|
||||
|
||||
if(GCSsys.isEmptyDiagnoseMatrix()) { // special case in which no other driving constraint is in the system
|
||||
for(auto geoid : blockedGeoIds) {
|
||||
switch(Geoms[geoid].type) {
|
||||
case Point:
|
||||
params_to_block.push_back(Points[Geoms[geoid].index].x);
|
||||
params_to_block.push_back(Points[Geoms[geoid].index].y);
|
||||
break;
|
||||
case Line:
|
||||
Lines[Geoms[geoid].index].PushOwnParams(params_to_block);
|
||||
break;
|
||||
case Arc:
|
||||
Arcs[Geoms[geoid].index].PushOwnParams(params_to_block);
|
||||
break;
|
||||
case Circle:
|
||||
Circles[Geoms[geoid].index].PushOwnParams(params_to_block);
|
||||
break;
|
||||
case Ellipse:
|
||||
Ellipses[Geoms[geoid].index].PushOwnParams(params_to_block);
|
||||
break;
|
||||
case ArcOfEllipse:
|
||||
ArcsOfEllipse[Geoms[geoid].index].PushOwnParams(params_to_block);
|
||||
break;
|
||||
case ArcOfHyperbola:
|
||||
ArcsOfHyperbola[Geoms[geoid].index].PushOwnParams(params_to_block);
|
||||
break;
|
||||
case ArcOfParabola:
|
||||
ArcsOfParabola[Geoms[geoid].index].PushOwnParams(params_to_block);
|
||||
break;
|
||||
case BSpline:
|
||||
BSplines[Geoms[geoid].index].PushOwnParams(params_to_block);
|
||||
break;
|
||||
case None:
|
||||
break;
|
||||
}
|
||||
bool unsatisfied_groups = analyseBlockedConstraintDependentParameters(blockedGeoIds, params_to_block);
|
||||
|
||||
// I am unsure if more than one QR iterations are needed with the current implementation.
|
||||
//
|
||||
// With previous implementations mostly one QR iteration was enough, but if block constraint is abused, more
|
||||
// iterations were needed.
|
||||
int index = 0;
|
||||
while(unsatisfied_groups) {
|
||||
// We tried hard not to arrive to an unsatisfied group, so we try harder
|
||||
// This loop has the advantage that the user will notice increased effort to solve,
|
||||
// so may understand he is abusing the block constraint, while guaranteing that wrong
|
||||
// behaviour of the block constraint is not undetected.
|
||||
|
||||
// Another QR iteration
|
||||
fixParametersAndDiagnose(params_to_block);
|
||||
|
||||
unsatisfied_groups = analyseBlockedConstraintDependentParameters(blockedGeoIds,params_to_block);
|
||||
|
||||
if (debugMode==GCS::IterationLevel) {
|
||||
Base::Console().Log("Sketcher::setUpSketch()-BlockConstraint-PostAnalysis:%d\n",index);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
else {
|
||||
#ifdef DEBUG_BLOCK_CONSTRAINT
|
||||
for(size_t i = 0; i < groups.size(); i++) {
|
||||
Base::Console().Log("\nDepParams: Group %d:",i);
|
||||
for(size_t j = 0; j < groups[i].size(); j++)
|
||||
Base::Console().Log("\n Param=%x ,GeoId=%d, GeoPos=%d",
|
||||
param2geoelement.find(*std::next(groups[i].begin(), j))->first,
|
||||
param2geoelement.find(*std::next(groups[i].begin(), j))->second.first,
|
||||
param2geoelement.find(*std::next(groups[i].begin(), j))->second.second);
|
||||
}
|
||||
#endif //DEBUG_BLOCK_CONSTRAINT
|
||||
|
||||
for(size_t i = 0; i < groups.size(); i++) {
|
||||
for(size_t j = 0; j < groups[i].size(); j++) {
|
||||
|
||||
double * thisparam = *std::next(groups[i].begin(), j);
|
||||
|
||||
auto element = param2geoelement.find(thisparam);
|
||||
|
||||
if (element != param2geoelement.end()) {
|
||||
|
||||
auto blocked = std::find(blockedGeoIds.begin(),blockedGeoIds.end(),element->second.first);
|
||||
|
||||
if( blocked != blockedGeoIds.end()) {
|
||||
// This dependent parameter group contains a parameter that should be blocked.
|
||||
//
|
||||
// One parameter per group is enough to fix the group, but it must be a different one for each group or
|
||||
// it will create a different dependency group
|
||||
auto already_in = std::find(params_to_block.begin(),params_to_block.end(), thisparam);
|
||||
|
||||
if( already_in == params_to_block.end()) {
|
||||
params_to_block.push_back(thisparam);
|
||||
|
||||
#ifdef DEBUG_BLOCK_CONSTRAINT
|
||||
Base::Console().Log("\nBlocking: Param=%x ,GeoId=%d, GeoPos=%d",
|
||||
element->first,
|
||||
element->second.first,
|
||||
element->second.second);
|
||||
#endif //DEBUG_BLOCK_CONSTRAINT
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // if(!GCSsys.isEmptyDiagnoseMatrix())
|
||||
|
||||
// 2. If something needs blocking, block-it
|
||||
if(params_to_block.size() > 0) {
|
||||
|
||||
for( auto p : params_to_block ) {
|
||||
auto findparam = std::find(Parameters.begin(),Parameters.end(), p);
|
||||
|
||||
if(findparam != Parameters.end()) {
|
||||
FixParameters.push_back(*findparam);
|
||||
Parameters.erase(findparam);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pDependencyGroups.clear();
|
||||
clearTemporaryConstraints();
|
||||
GCSsys.invalidatedDiagnosis();
|
||||
GCSsys.declareUnknowns(Parameters);
|
||||
GCSsys.declareDrivenParams(DrivenParameters);
|
||||
GCSsys.initSolution(defaultSolverRedundant);
|
||||
GCSsys.getConflicting(Conflicting);
|
||||
GCSsys.getRedundant(Redundant);
|
||||
GCSsys.getDependentParams(pDependentParametersList);
|
||||
|
||||
calculateDependentParametersElements();
|
||||
fixParametersAndDiagnose(params_to_block);
|
||||
|
||||
#ifdef DEBUG_BLOCK_CONSTRAINT
|
||||
std::vector < std::set < double*>> groups;
|
||||
if(params_to_block.size() > 0) {
|
||||
std::vector < std::vector < double*>> groups;
|
||||
GCSsys.getDependentParamsGroups(groups);
|
||||
|
||||
// Debug code block
|
||||
@@ -328,10 +322,17 @@ int Sketch::setUpSketch(const std::vector<Part::Geometry *> &GeoList,
|
||||
param2geoelement.find(*std::next(groups[i].begin(), j))->second.first,
|
||||
param2geoelement.find(*std::next(groups[i].begin(), j))->second.second);
|
||||
}
|
||||
#endif //DEBUG_BLOCK_CONSTRAINT
|
||||
}
|
||||
#endif //DEBUG_BLOCK_CONSTRAINT
|
||||
}
|
||||
|
||||
// Now we set the Sketch status with the latest solver information
|
||||
GCSsys.getConflicting(Conflicting);
|
||||
GCSsys.getRedundant(Redundant);
|
||||
GCSsys.getDependentParams(pDependentParametersList);
|
||||
|
||||
calculateDependentParametersElements();
|
||||
|
||||
if (debugMode==GCS::Minimal || debugMode==GCS::IterationLevel) {
|
||||
Base::TimeInfo end_time;
|
||||
|
||||
@@ -341,6 +342,112 @@ int Sketch::setUpSketch(const std::vector<Part::Geometry *> &GeoList,
|
||||
return GCSsys.dofsNumber();
|
||||
}
|
||||
|
||||
void Sketch::fixParametersAndDiagnose(std::vector<double *> ¶ms_to_block)
|
||||
{
|
||||
if(params_to_block.size() > 0) { // only there are parameters to fix
|
||||
for( auto p : params_to_block ) {
|
||||
auto findparam = std::find(Parameters.begin(),Parameters.end(), p);
|
||||
|
||||
if(findparam != Parameters.end()) {
|
||||
FixParameters.push_back(*findparam);
|
||||
Parameters.erase(findparam);
|
||||
}
|
||||
}
|
||||
|
||||
pDependencyGroups.clear();
|
||||
clearTemporaryConstraints();
|
||||
GCSsys.invalidatedDiagnosis();
|
||||
GCSsys.declareUnknowns(Parameters);
|
||||
GCSsys.declareDrivenParams(DrivenParameters);
|
||||
GCSsys.initSolution(defaultSolverRedundant);
|
||||
/*GCSsys.getConflicting(Conflicting);
|
||||
GCSsys.getRedundant(Redundant);
|
||||
GCSsys.getDependentParams(pDependentParametersList);
|
||||
|
||||
calculateDependentParametersElements();*/
|
||||
}
|
||||
}
|
||||
|
||||
bool Sketch::analyseBlockedConstraintDependentParameters(std::vector<int> &blockedGeoIds, std::vector<double *> ¶ms_to_block) const
|
||||
{
|
||||
// 1. Retrieve solver information
|
||||
std::vector < std::vector < double*>> groups;
|
||||
GCSsys.getDependentParamsGroups(groups);
|
||||
|
||||
// 2. Determine blockable parameters for each group (see documentation in header file).
|
||||
struct group {
|
||||
std::vector<double *> blockable_params_in_group;
|
||||
double * blocking_param_in_group = nullptr;
|
||||
};
|
||||
|
||||
std::vector<group> prop_groups(groups.size());
|
||||
|
||||
#ifdef DEBUG_BLOCK_CONSTRAINT
|
||||
for(size_t i = 0; i < groups.size(); i++) {
|
||||
Base::Console().Log("\nDepParams: Group %d:",i);
|
||||
for(size_t j = 0; j < groups[i].size(); j++)
|
||||
Base::Console().Log("\n Param=%x ,GeoId=%d, GeoPos=%d",
|
||||
param2geoelement.find(*std::next(groups[i].begin(), j))->first,
|
||||
param2geoelement.find(*std::next(groups[i].begin(), j))->second.first,
|
||||
param2geoelement.find(*std::next(groups[i].begin(), j))->second.second);
|
||||
}
|
||||
#endif //DEBUG_BLOCK_CONSTRAINT
|
||||
|
||||
for(size_t i = 0; i < groups.size(); i++) {
|
||||
for(size_t j = 0; j < groups[i].size(); j++) {
|
||||
|
||||
double * thisparam = *std::next(groups[i].begin(), j);
|
||||
|
||||
auto element = param2geoelement.find(thisparam);
|
||||
|
||||
if (element != param2geoelement.end()) {
|
||||
|
||||
auto blockable = std::find(blockedGeoIds.begin(),blockedGeoIds.end(),element->second.first);
|
||||
|
||||
if( blockable != blockedGeoIds.end()) {
|
||||
// This dependent parameter group contains at least one parameter that should be blocked, so added to the blockable list.
|
||||
prop_groups[i].blockable_params_in_group.push_back(thisparam);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Apply heuristic - pick the last blockable param available to block the group, starting from the last group
|
||||
for(size_t i = prop_groups.size(); i--> 0;) {
|
||||
for(size_t j = prop_groups[i].blockable_params_in_group.size(); j-->0; ) {
|
||||
// check if parameter is already satisfying one group
|
||||
double * thisparam = prop_groups[i].blockable_params_in_group[j];
|
||||
auto pos = std::find(params_to_block.begin(), params_to_block.end(), thisparam);
|
||||
|
||||
if( pos == params_to_block.end()) { // not found, so add
|
||||
params_to_block.push_back(thisparam);
|
||||
prop_groups[i].blocking_param_in_group = thisparam;
|
||||
#ifdef DEBUG_BLOCK_CONSTRAINT
|
||||
Base::Console().Log("\nTentatively blocking group %d, with param=%x", i, thisparam);
|
||||
#endif //DEBUG_BLOCK_CONSTRAINT
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Check if groups are satisfied or are licitly unsatisfiable and thus deemed as satisfied
|
||||
bool unsatisfied_groups = false;
|
||||
for(size_t i = 0; i < prop_groups.size(); i++) {
|
||||
// 4.1. unsatisfiable group
|
||||
if(prop_groups[i].blockable_params_in_group.size() == 0) {
|
||||
// this group does not contain any blockable parameter, so it is by definition satisfied (or impossible to satisfy by block constraints)
|
||||
continue;
|
||||
}
|
||||
// 4.2. satisfiable and not satisfied
|
||||
if(prop_groups[i].blocking_param_in_group == nullptr) {
|
||||
unsatisfied_groups = true;
|
||||
}
|
||||
}
|
||||
|
||||
return unsatisfied_groups;
|
||||
}
|
||||
|
||||
|
||||
void Sketch::clearTemporaryConstraints(void)
|
||||
{
|
||||
GCSsys.clearByTag(GCS::DefaultTemporaryConstraint);
|
||||
@@ -390,7 +497,7 @@ void Sketch::calculateDependentParametersElements(void)
|
||||
}
|
||||
}
|
||||
|
||||
std::vector < std::set < double*>> groups;
|
||||
std::vector < std::vector < double*>> groups;
|
||||
GCSsys.getDependentParamsGroups(groups);
|
||||
|
||||
pDependencyGroups.resize(groups.size());
|
||||
@@ -399,7 +506,7 @@ void Sketch::calculateDependentParametersElements(void)
|
||||
for(size_t i = 0; i < groups.size(); i++) {
|
||||
for(size_t j = 0; j < groups[i].size(); j++) {
|
||||
|
||||
auto element = param2geoelement.find(*std::next(groups[i].begin(), j));
|
||||
auto element = param2geoelement.find(groups[i][j]);
|
||||
|
||||
if (element != param2geoelement.end()) {
|
||||
pDependencyGroups[i].insert(element->second);
|
||||
|
||||
@@ -499,6 +499,71 @@ private:
|
||||
int checkGeoId(int geoId) const;
|
||||
GCS::Curve* getGCSCurveByGeoId(int geoId);
|
||||
const GCS::Curve* getGCSCurveByGeoId(int geoId) const;
|
||||
|
||||
// Block constraints
|
||||
|
||||
/** This function performs a pre-analysis of blocked geometries, separating them into:
|
||||
*
|
||||
* 1) onlyblockedGeometry : Geometries affected exclusively by a block constraint.
|
||||
*
|
||||
* 2) blockedGeoIds : Geometries affected notonly by a block constraint.
|
||||
*
|
||||
* This is important because 1) can be pre-fixed when creating geometry and constraints
|
||||
* before GCS::diagnose() via initSolution(). This is important because if no other constraint
|
||||
* affect the geometry, the geometry parameters won't even appear in the Jacobian, and they won't
|
||||
* be reported as dependent parameters.
|
||||
*
|
||||
* On the contrary 2) cannot be pre-fixed because it would lead to redundant constraints and requires
|
||||
* a post-analysis, see analyseBlockedConstraintDependentParameters, to fix just the parameters that
|
||||
* fulfil the dependacy groups.
|
||||
*/
|
||||
bool analyseBlockedGeometry( const std::vector<Part::Geometry *> &internalGeoList,
|
||||
const std::vector<Constraint *> &constraintList,
|
||||
std::vector<bool> &onlyblockedGeometry,
|
||||
std::vector<int> &blockedGeoIds) const;
|
||||
|
||||
/* This function performs a post-analysis of blocked geometries (see analyseBlockedGeometry for more detail
|
||||
* on the pre-analysis).
|
||||
*
|
||||
* Basically identifies which parameters shall be fixed to make geometries having blocking constraints fixed,
|
||||
* while not leading to redundant/conflicting constraints. These parameters must belong to blocked geometry. This
|
||||
* is, groups may comprise parameters belonging to blocked geometry and parameters belonging to unconstrained geometry.
|
||||
* It is licit that the latter remain as dependent parameters. The former are referred to as "blockable parameters".
|
||||
*
|
||||
* Extending this concept, there may be unsatisfiable groups (because they do not comprise any bloackable parameter),
|
||||
* and it is the desired outcome NOT to satisfy such groups.
|
||||
*
|
||||
* There is not a single combination of fixed parameters from the blockable parameters that satisfy all the dependency
|
||||
* groups. However:
|
||||
*
|
||||
* 1) some combinations do not satisfy all the dependency groups that must be satisfied (e.g. fixing one
|
||||
* group containing two blockable parameters with a given one may result in another group, fixable only by the former, not
|
||||
* to be satisfied). This leads, in a subsequent diagnosis, to satisfiable unsatisfied groups.
|
||||
*
|
||||
* 2) some combinations lead to partially redundant constraints, that the solver will silently drop in a subsequent diagnosis,
|
||||
* thereby reducing the rank of the system fixing less than it should.
|
||||
*
|
||||
* Implementation rationale (at this time):
|
||||
*
|
||||
* The implementation is on the order of the groups provided by the QR decomposition used to reveal the parameters
|
||||
* (see System::identifyDependentParameters in GCS). Zeros are made over the pilot of the full R matrix of the QR decomposition,
|
||||
* which is a top triangular matrix.This, together with the permutation matrix, allow to know groups of dependent parameters
|
||||
* (cols between rank and full size). Each group refers to a new parameter not affected by the rank in combination with other free
|
||||
* parameters intervening in the rank (because of the triangular shape of the R matrix). This results in that each the first column
|
||||
* between the rank and the full size, may only depend on a number of parameters, while the last full size colum may dependent on
|
||||
* any amount of previously introduced parameters.
|
||||
*
|
||||
* Thus the rationale is start from the last group (having **potentially** the larger amount of parameters) and selecting as blocking
|
||||
* for that group the latest blockable parameter. Because previous groups do not have access to the last parameter, this can never
|
||||
* interfere with previous groups. However, because the last parameter may not be a blockable one, there is a risk of selecting a parameter
|
||||
* common with other group, albeit the probability is reduced and probably (I have not demonstrated it though and I am not sure), it leads
|
||||
* to the right solution in one iteration.
|
||||
*
|
||||
*/
|
||||
bool analyseBlockedConstraintDependentParameters(std::vector<int> &blockedGeoIds, std::vector<double *> ¶ms_to_block) const;
|
||||
|
||||
/// utility function refactoring fixing the provided parameters and running a new diagnose
|
||||
void fixParametersAndDiagnose(std::vector<double *> ¶ms_to_block);
|
||||
};
|
||||
|
||||
} //namespace Part
|
||||
|
||||
@@ -226,6 +226,7 @@ public:
|
||||
void LogQRSystemInformation(const System &system, int paramsNum = 0, int constrNum = 0, int rank = 0);
|
||||
|
||||
void LogGroupOfConstraints(const std::string & str, std::vector< std::vector<Constraint *> > constraintgroups);
|
||||
void LogGroupOfParameters(const std::string & str, std::vector< std::vector<double *> > parametergroups);
|
||||
|
||||
void LogMatrix(const std::string str, Eigen::MatrixXd matrix);
|
||||
void LogMatrix(const std::string str, MatrixIndexType matrix);
|
||||
@@ -367,6 +368,23 @@ void SolverReportingManager::LogGroupOfConstraints(const std::string & str, std:
|
||||
LogString(tempstream.str());
|
||||
}
|
||||
|
||||
void SolverReportingManager::LogGroupOfParameters(const std::string & str, std::vector< std::vector<double *> > parametergroups)
|
||||
{
|
||||
std::stringstream tempstream;
|
||||
|
||||
tempstream << str << ":" << '\n';
|
||||
|
||||
for(size_t i = 0; i < parametergroups.size(); i++) {
|
||||
tempstream << "[";
|
||||
|
||||
for(auto p : parametergroups[i])
|
||||
tempstream << std::hex << p << " ";
|
||||
|
||||
tempstream << "]" << '\n';
|
||||
}
|
||||
|
||||
LogString(tempstream.str());
|
||||
}
|
||||
|
||||
#ifdef _GCS_DEBUG
|
||||
void SolverReportingManager::LogMatrix(const std::string str, Eigen::MatrixXd matrix)
|
||||
@@ -4017,9 +4035,9 @@ SolverReportingManager::Manager().LogToFile("GCS::System::diagnose()\n");
|
||||
//
|
||||
// Debug:
|
||||
// auto fut = std::async(std::launch::deferred,&System::identifyDependentParametersSparseQR,this,J,jacobianconstraintmap, pdiagnoselist, false);
|
||||
auto fut = std::async(&System::identifyDependentParametersSparseQR,this,J,jacobianconstraintmap, pdiagnoselist, false);
|
||||
auto fut = std::async(&System::identifyDependentParametersSparseQR,this,J,jacobianconstraintmap, pdiagnoselist, /*silent=*/true);
|
||||
|
||||
makeSparseQRDecomposition( J, jacobianconstraintmap, SqrJT, rank, R);
|
||||
makeSparseQRDecomposition( J, jacobianconstraintmap, SqrJT, rank, R, /*transposed=*/true, /*silent=*/false);
|
||||
|
||||
int paramsNum = SqrJT.rows();
|
||||
int constrNum = SqrJT.cols();
|
||||
@@ -4260,21 +4278,31 @@ void System::identifyDependentParameters( T & qrJ,
|
||||
if(!silent)
|
||||
SolverReportingManager::Manager().LogMatrix("Rparams_nonzeros_over_pilot", Rparams);
|
||||
#endif
|
||||
|
||||
pDependentParametersGroups.resize(qrJ.cols()-rank);
|
||||
for (int j=rank; j < qrJ.cols(); j++) {
|
||||
for (int row=0; row < rank; row++) {
|
||||
if (fabs(Rparams(row,j)) > 1e-10) {
|
||||
int origCol = qrJ.colsPermutation().indices()[row];
|
||||
|
||||
pDependentParametersGroups[j-rank].insert(pdiagnoselist[origCol]);
|
||||
pDependentParametersGroups[j-rank].push_back(pdiagnoselist[origCol]);
|
||||
pDependentParameters.push_back(pdiagnoselist[origCol]);
|
||||
}
|
||||
}
|
||||
int origCol = qrJ.colsPermutation().indices()[j];
|
||||
|
||||
pDependentParametersGroups[j-rank].insert(pdiagnoselist[origCol]);
|
||||
pDependentParametersGroups[j-rank].push_back(pdiagnoselist[origCol]);
|
||||
pDependentParameters.push_back(pdiagnoselist[origCol]);
|
||||
}
|
||||
|
||||
#ifdef _GCS_DEBUG
|
||||
if(!silent) {
|
||||
SolverReportingManager::Manager().LogMatrix("PermMatrix", (Eigen::MatrixXd)qrJ.colsPermutation());
|
||||
|
||||
SolverReportingManager::Manager().LogGroupOfParameters("ParameterGroups",pDependentParametersGroups);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void System::identifyDependentGeometryParametersInTransposedJacobianDenseQRDecomposition(
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace GCS
|
||||
|
||||
// This is a map of primary and secondary identifiers that are found dependent by the solver
|
||||
// GCS ignores from a type point
|
||||
std::vector< std::set<double *> > pDependentParametersGroups;
|
||||
std::vector< std::vector<double *> > pDependentParametersGroups;
|
||||
|
||||
std::vector<Constraint *> clist;
|
||||
std::map<Constraint *,VEC_pD > c2p; // constraint to parameter adjacency list
|
||||
@@ -363,7 +363,7 @@ namespace GCS
|
||||
{ redundantOut = hasDiagnosis ? redundantTags : VEC_I(0); }
|
||||
void getDependentParams(VEC_pD &pdependentparameterlist) const
|
||||
{ pdependentparameterlist = pDependentParameters;}
|
||||
void getDependentParamsGroups(std::vector<std::set<double *>> &pdependentparametergroups) const
|
||||
void getDependentParamsGroups(std::vector<std::vector<double *>> &pdependentparametergroups) const
|
||||
{ pdependentparametergroups = pDependentParametersGroups;}
|
||||
bool isEmptyDiagnoseMatrix() const {return emptyDiagnoseMatrix;}
|
||||
void invalidatedDiagnosis();
|
||||
|
||||
Reference in New Issue
Block a user