source: src/main/java/agents/anac/y2019/harddealer/math3/optim/linear/SimplexTableau.java

Last change on this file was 204, checked in by Katsuhide Fujita, 5 years ago

Fixed errors of ANAC2019 agents

  • Property svn:executable set to *
File size: 25.7 KB
Line 
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17package agents.anac.y2019.harddealer.math3.optim.linear;
18
19import java.io.IOException;
20import java.io.ObjectInputStream;
21import java.io.ObjectOutputStream;
22import java.io.Serializable;
23import java.util.ArrayList;
24import java.util.Arrays;
25import java.util.Collection;
26import java.util.HashSet;
27import java.util.List;
28import java.util.Set;
29import java.util.TreeSet;
30
31import agents.anac.y2019.harddealer.math3.linear.Array2DRowRealMatrix;
32import agents.anac.y2019.harddealer.math3.linear.MatrixUtils;
33import agents.anac.y2019.harddealer.math3.linear.RealVector;
34import agents.anac.y2019.harddealer.math3.optim.nonlinear.scalar.GoalType;
35import agents.anac.y2019.harddealer.math3.optim.PointValuePair;
36import agents.anac.y2019.harddealer.math3.util.Precision;
37
38/**
39 * A tableau for use in the Simplex method.
40 *
41 * <p>
42 * Example:
43 * <pre>
44 * W | Z | x1 | x2 | x- | s1 | s2 | a1 | RHS
45 * ---------------------------------------------------
46 * -1 0 0 0 0 0 0 1 0 &lt;= phase 1 objective
47 * 0 1 -15 -10 0 0 0 0 0 &lt;= phase 2 objective
48 * 0 0 1 0 0 1 0 0 2 &lt;= constraint 1
49 * 0 0 0 1 0 0 1 0 3 &lt;= constraint 2
50 * 0 0 1 1 0 0 0 1 4 &lt;= constraint 3
51 * </pre>
52 * W: Phase 1 objective function</br>
53 * Z: Phase 2 objective function</br>
54 * x1 &amp; x2: Decision variables</br>
55 * x-: Extra decision variable to allow for negative values</br>
56 * s1 &amp; s2: Slack/Surplus variables</br>
57 * a1: Artificial variable</br>
58 * RHS: Right hand side</br>
59 * </p>
60 * @since 2.0
61 */
62class SimplexTableau implements Serializable {
63
64 /** Column label for negative vars. */
65 private static final String NEGATIVE_VAR_COLUMN_LABEL = "x-";
66
67 /** Serializable version identifier. */
68 private static final long serialVersionUID = -1369660067587938365L;
69
70 /** Linear objective function. */
71 private final LinearObjectiveFunction f;
72
73 /** Linear constraints. */
74 private final List<LinearConstraint> constraints;
75
76 /** Whether to restrict the variables to non-negative values. */
77 private final boolean restrictToNonNegative;
78
79 /** The variables each column represents */
80 private final List<String> columnLabels = new ArrayList<String>();
81
82 /** Simple tableau. */
83 private transient Array2DRowRealMatrix tableau;
84
85 /** Number of decision variables. */
86 private final int numDecisionVariables;
87
88 /** Number of slack variables. */
89 private final int numSlackVariables;
90
91 /** Number of artificial variables. */
92 private int numArtificialVariables;
93
94 /** Amount of error to accept when checking for optimality. */
95 private final double epsilon;
96
97 /** Amount of error to accept in floating point comparisons. */
98 private final int maxUlps;
99
100 /** Maps basic variables to row they are basic in. */
101 private int[] basicVariables;
102
103 /** Maps rows to their corresponding basic variables. */
104 private int[] basicRows;
105
106 /**
107 * Builds a tableau for a linear problem.
108 *
109 * @param f Linear objective function.
110 * @param constraints Linear constraints.
111 * @param goalType Optimization goal: either {@link GoalType#MAXIMIZE}
112 * or {@link GoalType#MINIMIZE}.
113 * @param restrictToNonNegative Whether to restrict the variables to non-negative values.
114 * @param epsilon Amount of error to accept when checking for optimality.
115 */
116 SimplexTableau(final LinearObjectiveFunction f,
117 final Collection<LinearConstraint> constraints,
118 final GoalType goalType,
119 final boolean restrictToNonNegative,
120 final double epsilon) {
121 this(f, constraints, goalType, restrictToNonNegative, epsilon, SimplexSolver.DEFAULT_ULPS);
122 }
123
124 /**
125 * Build a tableau for a linear problem.
126 * @param f linear objective function
127 * @param constraints linear constraints
128 * @param goalType type of optimization goal: either {@link GoalType#MAXIMIZE} or {@link GoalType#MINIMIZE}
129 * @param restrictToNonNegative whether to restrict the variables to non-negative values
130 * @param epsilon amount of error to accept when checking for optimality
131 * @param maxUlps amount of error to accept in floating point comparisons
132 */
133 SimplexTableau(final LinearObjectiveFunction f,
134 final Collection<LinearConstraint> constraints,
135 final GoalType goalType,
136 final boolean restrictToNonNegative,
137 final double epsilon,
138 final int maxUlps) {
139 this.f = f;
140 this.constraints = normalizeConstraints(constraints);
141 this.restrictToNonNegative = restrictToNonNegative;
142 this.epsilon = epsilon;
143 this.maxUlps = maxUlps;
144 this.numDecisionVariables = f.getCoefficients().getDimension() + (restrictToNonNegative ? 0 : 1);
145 this.numSlackVariables = getConstraintTypeCounts(Relationship.LEQ) +
146 getConstraintTypeCounts(Relationship.GEQ);
147 this.numArtificialVariables = getConstraintTypeCounts(Relationship.EQ) +
148 getConstraintTypeCounts(Relationship.GEQ);
149 this.tableau = createTableau(goalType == GoalType.MAXIMIZE);
150 // initialize the basic variables for phase 1:
151 // we know that only slack or artificial variables can be basic
152 initializeBasicVariables(getSlackVariableOffset());
153 initializeColumnLabels();
154 }
155
156 /**
157 * Initialize the labels for the columns.
158 */
159 protected void initializeColumnLabels() {
160 if (getNumObjectiveFunctions() == 2) {
161 columnLabels.add("W");
162 }
163 columnLabels.add("Z");
164 for (int i = 0; i < getOriginalNumDecisionVariables(); i++) {
165 columnLabels.add("x" + i);
166 }
167 if (!restrictToNonNegative) {
168 columnLabels.add(NEGATIVE_VAR_COLUMN_LABEL);
169 }
170 for (int i = 0; i < getNumSlackVariables(); i++) {
171 columnLabels.add("s" + i);
172 }
173 for (int i = 0; i < getNumArtificialVariables(); i++) {
174 columnLabels.add("a" + i);
175 }
176 columnLabels.add("RHS");
177 }
178
179 /**
180 * Create the tableau by itself.
181 * @param maximize if true, goal is to maximize the objective function
182 * @return created tableau
183 */
184 protected Array2DRowRealMatrix createTableau(final boolean maximize) {
185
186 // create a matrix of the correct size
187 int width = numDecisionVariables + numSlackVariables +
188 numArtificialVariables + getNumObjectiveFunctions() + 1; // + 1 is for RHS
189 int height = constraints.size() + getNumObjectiveFunctions();
190 Array2DRowRealMatrix matrix = new Array2DRowRealMatrix(height, width);
191
192 // initialize the objective function rows
193 if (getNumObjectiveFunctions() == 2) {
194 matrix.setEntry(0, 0, -1);
195 }
196
197 int zIndex = (getNumObjectiveFunctions() == 1) ? 0 : 1;
198 matrix.setEntry(zIndex, zIndex, maximize ? 1 : -1);
199 RealVector objectiveCoefficients = maximize ? f.getCoefficients().mapMultiply(-1) : f.getCoefficients();
200 copyArray(objectiveCoefficients.toArray(), matrix.getDataRef()[zIndex]);
201 matrix.setEntry(zIndex, width - 1, maximize ? f.getConstantTerm() : -1 * f.getConstantTerm());
202
203 if (!restrictToNonNegative) {
204 matrix.setEntry(zIndex, getSlackVariableOffset() - 1,
205 getInvertedCoefficientSum(objectiveCoefficients));
206 }
207
208 // initialize the constraint rows
209 int slackVar = 0;
210 int artificialVar = 0;
211 for (int i = 0; i < constraints.size(); i++) {
212 LinearConstraint constraint = constraints.get(i);
213 int row = getNumObjectiveFunctions() + i;
214
215 // decision variable coefficients
216 copyArray(constraint.getCoefficients().toArray(), matrix.getDataRef()[row]);
217
218 // x-
219 if (!restrictToNonNegative) {
220 matrix.setEntry(row, getSlackVariableOffset() - 1,
221 getInvertedCoefficientSum(constraint.getCoefficients()));
222 }
223
224 // RHS
225 matrix.setEntry(row, width - 1, constraint.getValue());
226
227 // slack variables
228 if (constraint.getRelationship() == Relationship.LEQ) {
229 matrix.setEntry(row, getSlackVariableOffset() + slackVar++, 1); // slack
230 } else if (constraint.getRelationship() == Relationship.GEQ) {
231 matrix.setEntry(row, getSlackVariableOffset() + slackVar++, -1); // excess
232 }
233
234 // artificial variables
235 if ((constraint.getRelationship() == Relationship.EQ) ||
236 (constraint.getRelationship() == Relationship.GEQ)) {
237 matrix.setEntry(0, getArtificialVariableOffset() + artificialVar, 1);
238 matrix.setEntry(row, getArtificialVariableOffset() + artificialVar++, 1);
239 matrix.setRowVector(0, matrix.getRowVector(0).subtract(matrix.getRowVector(row)));
240 }
241 }
242
243 return matrix;
244 }
245
246 /**
247 * Get new versions of the constraints which have positive right hand sides.
248 * @param originalConstraints original (not normalized) constraints
249 * @return new versions of the constraints
250 */
251 public List<LinearConstraint> normalizeConstraints(Collection<LinearConstraint> originalConstraints) {
252 List<LinearConstraint> normalized = new ArrayList<LinearConstraint>(originalConstraints.size());
253 for (LinearConstraint constraint : originalConstraints) {
254 normalized.add(normalize(constraint));
255 }
256 return normalized;
257 }
258
259 /**
260 * Get a new equation equivalent to this one with a positive right hand side.
261 * @param constraint reference constraint
262 * @return new equation
263 */
264 private LinearConstraint normalize(final LinearConstraint constraint) {
265 if (constraint.getValue() < 0) {
266 return new LinearConstraint(constraint.getCoefficients().mapMultiply(-1),
267 constraint.getRelationship().oppositeRelationship(),
268 -1 * constraint.getValue());
269 }
270 return new LinearConstraint(constraint.getCoefficients(),
271 constraint.getRelationship(), constraint.getValue());
272 }
273
274 /**
275 * Get the number of objective functions in this tableau.
276 * @return 2 for Phase 1. 1 for Phase 2.
277 */
278 protected final int getNumObjectiveFunctions() {
279 return this.numArtificialVariables > 0 ? 2 : 1;
280 }
281
282 /**
283 * Get a count of constraints corresponding to a specified relationship.
284 * @param relationship relationship to count
285 * @return number of constraint with the specified relationship
286 */
287 private int getConstraintTypeCounts(final Relationship relationship) {
288 int count = 0;
289 for (final LinearConstraint constraint : constraints) {
290 if (constraint.getRelationship() == relationship) {
291 ++count;
292 }
293 }
294 return count;
295 }
296
297 /**
298 * Get the -1 times the sum of all coefficients in the given array.
299 * @param coefficients coefficients to sum
300 * @return the -1 times the sum of all coefficients in the given array.
301 */
302 protected static double getInvertedCoefficientSum(final RealVector coefficients) {
303 double sum = 0;
304 for (double coefficient : coefficients.toArray()) {
305 sum -= coefficient;
306 }
307 return sum;
308 }
309
310 /**
311 * Checks whether the given column is basic.
312 * @param col index of the column to check
313 * @return the row that the variable is basic in. null if the column is not basic
314 */
315 protected Integer getBasicRow(final int col) {
316 final int row = basicVariables[col];
317 return row == -1 ? null : row;
318 }
319
320 /**
321 * Returns the variable that is basic in this row.
322 * @param row the index of the row to check
323 * @return the variable that is basic for this row.
324 */
325 protected int getBasicVariable(final int row) {
326 return basicRows[row];
327 }
328
329 /**
330 * Initializes the basic variable / row mapping.
331 * @param startColumn the column to start
332 */
333 private void initializeBasicVariables(final int startColumn) {
334 basicVariables = new int[getWidth() - 1];
335 basicRows = new int[getHeight()];
336
337 Arrays.fill(basicVariables, -1);
338
339 for (int i = startColumn; i < getWidth() - 1; i++) {
340 Integer row = findBasicRow(i);
341 if (row != null) {
342 basicVariables[i] = row;
343 basicRows[row] = i;
344 }
345 }
346 }
347
348 /**
349 * Returns the row in which the given column is basic.
350 * @param col index of the column
351 * @return the row that the variable is basic in, or {@code null} if the variable is not basic.
352 */
353 private Integer findBasicRow(final int col) {
354 Integer row = null;
355 for (int i = 0; i < getHeight(); i++) {
356 final double entry = getEntry(i, col);
357 if (Precision.equals(entry, 1d, maxUlps) && (row == null)) {
358 row = i;
359 } else if (!Precision.equals(entry, 0d, maxUlps)) {
360 return null;
361 }
362 }
363 return row;
364 }
365
366 /**
367 * Removes the phase 1 objective function, positive cost non-artificial variables,
368 * and the non-basic artificial variables from this tableau.
369 */
370 protected void dropPhase1Objective() {
371 if (getNumObjectiveFunctions() == 1) {
372 return;
373 }
374
375 final Set<Integer> columnsToDrop = new TreeSet<Integer>();
376 columnsToDrop.add(0);
377
378 // positive cost non-artificial variables
379 for (int i = getNumObjectiveFunctions(); i < getArtificialVariableOffset(); i++) {
380 final double entry = getEntry(0, i);
381 if (Precision.compareTo(entry, 0d, epsilon) > 0) {
382 columnsToDrop.add(i);
383 }
384 }
385
386 // non-basic artificial variables
387 for (int i = 0; i < getNumArtificialVariables(); i++) {
388 int col = i + getArtificialVariableOffset();
389 if (getBasicRow(col) == null) {
390 columnsToDrop.add(col);
391 }
392 }
393
394 final double[][] matrix = new double[getHeight() - 1][getWidth() - columnsToDrop.size()];
395 for (int i = 1; i < getHeight(); i++) {
396 int col = 0;
397 for (int j = 0; j < getWidth(); j++) {
398 if (!columnsToDrop.contains(j)) {
399 matrix[i - 1][col++] = getEntry(i, j);
400 }
401 }
402 }
403
404 // remove the columns in reverse order so the indices are correct
405 Integer[] drop = columnsToDrop.toArray(new Integer[columnsToDrop.size()]);
406 for (int i = drop.length - 1; i >= 0; i--) {
407 columnLabels.remove((int) drop[i]);
408 }
409
410 this.tableau = new Array2DRowRealMatrix(matrix);
411 this.numArtificialVariables = 0;
412 // need to update the basic variable mappings as row/columns have been dropped
413 initializeBasicVariables(getNumObjectiveFunctions());
414 }
415
416 /**
417 * @param src the source array
418 * @param dest the destination array
419 */
420 private void copyArray(final double[] src, final double[] dest) {
421 System.arraycopy(src, 0, dest, getNumObjectiveFunctions(), src.length);
422 }
423
424 /**
425 * Returns whether the problem is at an optimal state.
426 * @return whether the model has been solved
427 */
428 boolean isOptimal() {
429 final double[] objectiveFunctionRow = getRow(0);
430 final int end = getRhsOffset();
431 for (int i = getNumObjectiveFunctions(); i < end; i++) {
432 final double entry = objectiveFunctionRow[i];
433 if (Precision.compareTo(entry, 0d, epsilon) < 0) {
434 return false;
435 }
436 }
437 return true;
438 }
439
440 /**
441 * Get the current solution.
442 * @return current solution
443 */
444 protected PointValuePair getSolution() {
445 int negativeVarColumn = columnLabels.indexOf(NEGATIVE_VAR_COLUMN_LABEL);
446 Integer negativeVarBasicRow = negativeVarColumn > 0 ? getBasicRow(negativeVarColumn) : null;
447 double mostNegative = negativeVarBasicRow == null ? 0 : getEntry(negativeVarBasicRow, getRhsOffset());
448
449 final Set<Integer> usedBasicRows = new HashSet<Integer>();
450 final double[] coefficients = new double[getOriginalNumDecisionVariables()];
451 for (int i = 0; i < coefficients.length; i++) {
452 int colIndex = columnLabels.indexOf("x" + i);
453 if (colIndex < 0) {
454 coefficients[i] = 0;
455 continue;
456 }
457 Integer basicRow = getBasicRow(colIndex);
458 if (basicRow != null && basicRow == 0) {
459 // if the basic row is found to be the objective function row
460 // set the coefficient to 0 -> this case handles unconstrained
461 // variables that are still part of the objective function
462 coefficients[i] = 0;
463 } else if (usedBasicRows.contains(basicRow)) {
464 // if multiple variables can take a given value
465 // then we choose the first and set the rest equal to 0
466 coefficients[i] = 0 - (restrictToNonNegative ? 0 : mostNegative);
467 } else {
468 usedBasicRows.add(basicRow);
469 coefficients[i] =
470 (basicRow == null ? 0 : getEntry(basicRow, getRhsOffset())) -
471 (restrictToNonNegative ? 0 : mostNegative);
472 }
473 }
474 return new PointValuePair(coefficients, f.value(coefficients));
475 }
476
477 /**
478 * Perform the row operations of the simplex algorithm with the selected
479 * pivot column and row.
480 * @param pivotCol the pivot column
481 * @param pivotRow the pivot row
482 */
483 protected void performRowOperations(int pivotCol, int pivotRow) {
484 // set the pivot element to 1
485 final double pivotVal = getEntry(pivotRow, pivotCol);
486 divideRow(pivotRow, pivotVal);
487
488 // set the rest of the pivot column to 0
489 for (int i = 0; i < getHeight(); i++) {
490 if (i != pivotRow) {
491 final double multiplier = getEntry(i, pivotCol);
492 if (multiplier != 0.0) {
493 subtractRow(i, pivotRow, multiplier);
494 }
495 }
496 }
497
498 // update the basic variable mappings
499 final int previousBasicVariable = getBasicVariable(pivotRow);
500 basicVariables[previousBasicVariable] = -1;
501 basicVariables[pivotCol] = pivotRow;
502 basicRows[pivotRow] = pivotCol;
503 }
504
505 /**
506 * Divides one row by a given divisor.
507 * <p>
508 * After application of this operation, the following will hold:
509 * <pre>dividendRow = dividendRow / divisor</pre>
510 *
511 * @param dividendRowIndex index of the row
512 * @param divisor value of the divisor
513 */
514 protected void divideRow(final int dividendRowIndex, final double divisor) {
515 final double[] dividendRow = getRow(dividendRowIndex);
516 for (int j = 0; j < getWidth(); j++) {
517 dividendRow[j] /= divisor;
518 }
519 }
520
521 /**
522 * Subtracts a multiple of one row from another.
523 * <p>
524 * After application of this operation, the following will hold:
525 * <pre>minuendRow = minuendRow - multiple * subtrahendRow</pre>
526 *
527 * @param minuendRowIndex row index
528 * @param subtrahendRowIndex row index
529 * @param multiplier multiplication factor
530 */
531 protected void subtractRow(final int minuendRowIndex, final int subtrahendRowIndex, final double multiplier) {
532 final double[] minuendRow = getRow(minuendRowIndex);
533 final double[] subtrahendRow = getRow(subtrahendRowIndex);
534 for (int i = 0; i < getWidth(); i++) {
535 minuendRow[i] -= subtrahendRow[i] * multiplier;
536 }
537 }
538
539 /**
540 * Get the width of the tableau.
541 * @return width of the tableau
542 */
543 protected final int getWidth() {
544 return tableau.getColumnDimension();
545 }
546
547 /**
548 * Get the height of the tableau.
549 * @return height of the tableau
550 */
551 protected final int getHeight() {
552 return tableau.getRowDimension();
553 }
554
555 /**
556 * Get an entry of the tableau.
557 * @param row row index
558 * @param column column index
559 * @return entry at (row, column)
560 */
561 protected final double getEntry(final int row, final int column) {
562 return tableau.getEntry(row, column);
563 }
564
565 /**
566 * Set an entry of the tableau.
567 * @param row row index
568 * @param column column index
569 * @param value for the entry
570 */
571 protected final void setEntry(final int row, final int column, final double value) {
572 tableau.setEntry(row, column, value);
573 }
574
575 /**
576 * Get the offset of the first slack variable.
577 * @return offset of the first slack variable
578 */
579 protected final int getSlackVariableOffset() {
580 return getNumObjectiveFunctions() + numDecisionVariables;
581 }
582
583 /**
584 * Get the offset of the first artificial variable.
585 * @return offset of the first artificial variable
586 */
587 protected final int getArtificialVariableOffset() {
588 return getNumObjectiveFunctions() + numDecisionVariables + numSlackVariables;
589 }
590
591 /**
592 * Get the offset of the right hand side.
593 * @return offset of the right hand side
594 */
595 protected final int getRhsOffset() {
596 return getWidth() - 1;
597 }
598
599 /**
600 * Get the number of decision variables.
601 * <p>
602 * If variables are not restricted to positive values, this will include 1 extra decision variable to represent
603 * the absolute value of the most negative variable.
604 *
605 * @return number of decision variables
606 * @see #getOriginalNumDecisionVariables()
607 */
608 protected final int getNumDecisionVariables() {
609 return numDecisionVariables;
610 }
611
612 /**
613 * Get the original number of decision variables.
614 * @return original number of decision variables
615 * @see #getNumDecisionVariables()
616 */
617 protected final int getOriginalNumDecisionVariables() {
618 return f.getCoefficients().getDimension();
619 }
620
621 /**
622 * Get the number of slack variables.
623 * @return number of slack variables
624 */
625 protected final int getNumSlackVariables() {
626 return numSlackVariables;
627 }
628
629 /**
630 * Get the number of artificial variables.
631 * @return number of artificial variables
632 */
633 protected final int getNumArtificialVariables() {
634 return numArtificialVariables;
635 }
636
637 /**
638 * Get the row from the tableau.
639 * @param row the row index
640 * @return the reference to the underlying row data
641 */
642 protected final double[] getRow(int row) {
643 return tableau.getDataRef()[row];
644 }
645
646 /**
647 * Get the tableau data.
648 * @return tableau data
649 */
650 protected final double[][] getData() {
651 return tableau.getData();
652 }
653
654 /** {@inheritDoc} */
655 @Override
656 public boolean equals(Object other) {
657
658 if (this == other) {
659 return true;
660 }
661
662 if (other instanceof SimplexTableau) {
663 SimplexTableau rhs = (SimplexTableau) other;
664 return (restrictToNonNegative == rhs.restrictToNonNegative) &&
665 (numDecisionVariables == rhs.numDecisionVariables) &&
666 (numSlackVariables == rhs.numSlackVariables) &&
667 (numArtificialVariables == rhs.numArtificialVariables) &&
668 (epsilon == rhs.epsilon) &&
669 (maxUlps == rhs.maxUlps) &&
670 f.equals(rhs.f) &&
671 constraints.equals(rhs.constraints) &&
672 tableau.equals(rhs.tableau);
673 }
674 return false;
675 }
676
677 /** {@inheritDoc} */
678 @Override
679 public int hashCode() {
680 return Boolean.valueOf(restrictToNonNegative).hashCode() ^
681 numDecisionVariables ^
682 numSlackVariables ^
683 numArtificialVariables ^
684 Double.valueOf(epsilon).hashCode() ^
685 maxUlps ^
686 f.hashCode() ^
687 constraints.hashCode() ^
688 tableau.hashCode();
689 }
690
691 /**
692 * Serialize the instance.
693 * @param oos stream where object should be written
694 * @throws IOException if object cannot be written to stream
695 */
696 private void writeObject(ObjectOutputStream oos)
697 throws IOException {
698 oos.defaultWriteObject();
699 MatrixUtils.serializeRealMatrix(tableau, oos);
700 }
701
702 /**
703 * Deserialize the instance.
704 * @param ois stream from which the object should be read
705 * @throws ClassNotFoundException if a class in the stream cannot be found
706 * @throws IOException if object cannot be read from the stream
707 */
708 private void readObject(ObjectInputStream ois)
709 throws ClassNotFoundException, IOException {
710 ois.defaultReadObject();
711 MatrixUtils.deserializeRealMatrix(this, "tableau", ois);
712 }
713}
Note: See TracBrowser for help on using the repository browser.