|
1 | 1 | package symbolic
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "fmt" |
| 5 | + |
4 | 6 | "github.com/MatProGo-dev/SymbolicMath.go/smErrors"
|
5 | 7 | "gonum.org/v1/gonum/mat"
|
6 | 8 | )
|
@@ -281,3 +283,165 @@ func (sc ScalarConstraint) String() string {
|
281 | 283 | // Create the string representation
|
282 | 284 | return sc.LeftHandSide.String() + " " + sc.Sense.String() + " " + sc.RightHandSide.String()
|
283 | 285 | }
|
| 286 | + |
| 287 | +/* |
| 288 | +Simplify |
| 289 | +Description: |
| 290 | +
|
| 291 | + Simplifies the constraint by moving all variables to the left hand side and the constants to the right. |
| 292 | +*/ |
| 293 | +func (sc ScalarConstraint) AsSimplifiedConstraint() Constraint { |
| 294 | + return sc.Simplify() |
| 295 | +} |
| 296 | + |
| 297 | +func (sc ScalarConstraint) Variables() []Variable { |
| 298 | + return VariablesInThisConstraint(sc) |
| 299 | +} |
| 300 | + |
| 301 | +func (sc ScalarConstraint) ScaleBy(factor float64) Constraint { |
| 302 | + // Check that the constraint is well formed. |
| 303 | + err := sc.Check() |
| 304 | + if err != nil { |
| 305 | + panic(err) |
| 306 | + } |
| 307 | + |
| 308 | + // Scale the left hand side |
| 309 | + newLHS := sc.LeftHandSide.Multiply(factor).(ScalarExpression) |
| 310 | + |
| 311 | + // Scale the right hand side |
| 312 | + newRHS := sc.RightHandSide.Multiply(factor).(ScalarExpression) |
| 313 | + |
| 314 | + // If the factor is negative, then flip the sense of the constraint |
| 315 | + newSense := sc.Sense |
| 316 | + if factor < 0 { |
| 317 | + if sc.Sense == SenseLessThanEqual { |
| 318 | + newSense = SenseGreaterThanEqual |
| 319 | + } else if sc.Sense == SenseGreaterThanEqual { |
| 320 | + newSense = SenseLessThanEqual |
| 321 | + } |
| 322 | + } |
| 323 | + |
| 324 | + // Return the new constraint |
| 325 | + return ScalarConstraint{ |
| 326 | + LeftHandSide: newLHS, |
| 327 | + RightHandSide: newRHS, |
| 328 | + Sense: newSense, |
| 329 | + } |
| 330 | +} |
| 331 | + |
| 332 | +/* |
| 333 | +ImpliesThisIsAlsoSatisfied |
| 334 | +Description: |
| 335 | +
|
| 336 | + Returns true if this constraint implies that the other constraint is also satisfied. |
| 337 | +*/ |
| 338 | +func (sc ScalarConstraint) ImpliesThisIsAlsoSatisfied(other Constraint) bool { |
| 339 | + // Check that the constraint is well formed. |
| 340 | + err := sc.Check() |
| 341 | + if err != nil { |
| 342 | + panic(err) |
| 343 | + } |
| 344 | + |
| 345 | + // Check that the other constraint is well formed. |
| 346 | + err = other.Check() |
| 347 | + if err != nil { |
| 348 | + panic(err) |
| 349 | + } |
| 350 | + |
| 351 | + // Simplify both constraints |
| 352 | + sc = sc.Simplify() |
| 353 | + |
| 354 | + switch otherC := other.(type) { |
| 355 | + case ScalarConstraint: |
| 356 | + otherC = otherC.Simplify() |
| 357 | + |
| 358 | + // Naive implication check: |
| 359 | + // 1. Both constraints contain only 1 variable AND it is the same variable. Then, simply check the bounds. |
| 360 | + containsOneVar := len(sc.Variables()) == 1 && len(otherC.Variables()) == 1 |
| 361 | + scAndOtherShareSameVar := len(UnionOfVariables(sc.Variables(), otherC.Variables())) == 1 |
| 362 | + |
| 363 | + if containsOneVar && scAndOtherShareSameVar { |
| 364 | + // Get the coefficient of the single variable |
| 365 | + scCoeffVector := sc.LeftHandSide.LinearCoeff(sc.Variables()) |
| 366 | + scCoeff := scCoeffVector.AtVec(0) |
| 367 | + otherCCoeffVector := otherC.LeftHandSide.LinearCoeff(otherC.Variables()) |
| 368 | + otherCCoeff := otherCCoeffVector.AtVec(0) |
| 369 | + |
| 370 | + // If the coefficient of scCoeff is < 0, |
| 371 | + // then flip the signs of both sides of the constraint |
| 372 | + if scCoeff < 0 { |
| 373 | + sc = sc.ScaleBy(-1).(ScalarConstraint) |
| 374 | + } |
| 375 | + |
| 376 | + if otherCCoeff < 0 { |
| 377 | + otherC = otherC.ScaleBy(-1).(ScalarConstraint) |
| 378 | + } |
| 379 | + |
| 380 | + // The implication holds if all of the following are true: |
| 381 | + // 1. The sense of sc and otherC are either the same (or one is equality) |
| 382 | + // 2. The bounds of the constraint with the LessThanEqual or GreaterThanEqual sense are within the bounds of the other constraint. |
| 383 | + sensesAreCompatible := sc.Sense == otherC.Sense || |
| 384 | + sc.Sense == SenseEqual || |
| 385 | + otherC.Sense == SenseEqual |
| 386 | + |
| 387 | + if !sensesAreCompatible { |
| 388 | + return false |
| 389 | + } |
| 390 | + |
| 391 | + switch sc.Sense { |
| 392 | + case SenseLessThanEqual: |
| 393 | + // Check the senses of otherC |
| 394 | + switch otherC.Sense { |
| 395 | + case SenseLessThanEqual: |
| 396 | + // Both are <= |
| 397 | + // Then the implication holds if the upper bound of sc is <= the upper bound of otherC |
| 398 | + return sc.RightHandSide.Constant() <= otherC.RightHandSide.Constant() |
| 399 | + default: |
| 400 | + // sc is <= and otherC is either >= or == |
| 401 | + // Then the implication holds if the upper bound of sc is <= the lower bound of otherC |
| 402 | + return false |
| 403 | + } |
| 404 | + case SenseGreaterThanEqual: |
| 405 | + // Check the senses of otherC |
| 406 | + switch otherC.Sense { |
| 407 | + case SenseGreaterThanEqual: |
| 408 | + // Both are >= |
| 409 | + // Then the implication holds if the lower bound of sc is >= the lower bound of otherC |
| 410 | + return sc.RightHandSide.Constant() >= otherC.RightHandSide.Constant() |
| 411 | + default: |
| 412 | + // sc is >= and otherC is either <= or == |
| 413 | + // Then the implication holds if the lower bound of sc is >= the upper bound of otherC |
| 414 | + return false |
| 415 | + } |
| 416 | + case SenseEqual: |
| 417 | + // Check the senses of otherC |
| 418 | + switch otherC.Sense { |
| 419 | + case SenseEqual: |
| 420 | + // Both are == |
| 421 | + // Then the implication holds if the bounds are equal |
| 422 | + return sc.RightHandSide.Constant() == otherC.RightHandSide.Constant() |
| 423 | + case SenseLessThanEqual: |
| 424 | + // sc is == and otherC is <= |
| 425 | + // Then the implication holds if the bound of sc is <= the upper bound of otherC |
| 426 | + return sc.RightHandSide.Constant() <= otherC.RightHandSide.Constant() |
| 427 | + case SenseGreaterThanEqual: |
| 428 | + // sc is == and otherC is >= |
| 429 | + // Then the implication holds if the bound of sc is >= the lower bound of otherC |
| 430 | + return sc.RightHandSide.Constant() >= otherC.RightHandSide.Constant() |
| 431 | + } |
| 432 | + default: |
| 433 | + panic("unreachable code reached in ScalarConstraint.ImpliesThisIsAlsoSatisfied") |
| 434 | + } |
| 435 | + } |
| 436 | + case VectorConstraint, MatrixConstraint: |
| 437 | + // TODO: Implement more advanced implication checks. |
| 438 | + return false |
| 439 | + default: |
| 440 | + // Other types of constraints are not currently supported. |
| 441 | + panic( |
| 442 | + fmt.Errorf("implication checking between ScalarConstraint and %T is not currently supported", other), |
| 443 | + ) |
| 444 | + } |
| 445 | + |
| 446 | + return false |
| 447 | +} |
0 commit comments