Skip to content

Commit

Permalink
Merge pull request #8 from idaholab/mandd/knapsackModels
Browse files Browse the repository at this point in the history
Base Knapsack Model: used as external model that can be combined with genetic algorithm for Knapsack problem optimization.
  • Loading branch information
wangcj05 authored Feb 4, 2021
2 parents 5eb1fb7 + eb07c61 commit cc134f3
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 0 deletions.
1 change: 1 addition & 0 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
from LOGOS.src import CapitalInvestmentModel
from LOGOS.src import BatteryReplacementCashFlowModel
from LOGOS.src import IncrementalNPV
from LOGOS.src.knapsack import BaseKnapsackModel
50 changes: 50 additions & 0 deletions doc/user_manual/include/Knapsack.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
\section{Knapsack Models}
\label{sec:KnapsackModels}

LOGOS contains a set of Knapsack models that can be used coupled with RAVEN when
the desired optimization problem requires the use of specific models to generate
knapsack required parameters.
More specifically, all these models would be contained in a RAVEN EnsembleModel
and RAVEN optimization methods (e.g., Genetic Algorithms) would be employed to
find the optimal solution.


\subsection{BaseKnapsackModel}
\label{subsec:BaseKnapsackModel}
This model considers the classical Knapsack Model characterized by a set of elements
that can be chosen (or not).
The goal is to maximize the sum of the chosen element values provided that the sum of
element cost values satisfy capacity constraint (specified in the \xmlNode{capacity} node).

The ID of the variables that represent cost, value, and choice of each element are
indicated in the \xmlNode{map} node.
The model generates two variables:
\begin{itemize}
\item the validity of the chosen solution (specified in the \xmlNode{outcome} node): either
valid (i.e., 0), or invalid (i.e., 1) if the capacity constraint is not satisfied,
\item totalValue (specified in the \xmlNode{choiceValue} node): sum of the values of the
chosen elements
\end{itemize}

When calculating the \xmlNode{choiceValue} variable, if the \xmlNode{capacity} constraint
is not satisfied, then the \xmlNode{choiceValue} variable is penalized by multiplying the
project value by -\xmlNode{penaltyFactor}.

Example LOGOS input XML for DRO:
\begin{lstlisting}[style=XML]
<Models>
<ExternalModel name="knapsack" subType="LOGOS.BaseKnapsackModel">
<variables>element1Status,element2Status,element3Status,
element1Val ,element2Val ,element3Val,
element1Cost ,element2Cost ,element3Cost,
validity ,totalValue </variables>
<capacity>10</capacity>
<penaltyFactor>1.</penaltyFactor>
<outcome>validity</outcome>
<choiceValue>totalValue</choiceValue>
<map value='element1Val' cost='element1Cost' >element1Status</map>
<map value='element2Val' cost='element2Cost' >element2Status</map>
<map value='element3Val' cost='element3Cost' >element3Status</map>
</ExternalModel>
</Models>
\end{lstlisting}
1 change: 1 addition & 0 deletions doc/user_manual/user_manual.tex
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@
\input{include/CVaR.tex}
\input{include/PluginForRavenCode.tex}
\input{include/CashflowAndNPVModels.tex}
\input{include/knapsack.tex}

\section*{Document Version Information}
This document has been compiled using the following version of the plugin Git repository:
Expand Down
105 changes: 105 additions & 0 deletions src/knapsack/BaseKnapsackModel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Copyright 2020, Battelle Energy Alliance, LLC
# ALL RIGHTS RESERVED
"""
Created on February 2, 2021
@author: mandd
"""

#External Modules---------------------------------------------------------------
import numpy as np
import math
import copy
#External Modules End-----------------------------------------------------------

#Internal Modules---------------------------------------------------------------
from PluginsBaseClasses.ExternalModelPluginBase import ExternalModelPluginBase
#Internal Modules End-----------------------------------------------------------


class BaseKnapsackModel(ExternalModelPluginBase):
"""
This class is designed to create the BaseKnapsack model
"""
def __init__(self):
"""
Constructor
@ In, None
@ Out, None
"""
ExternalModelPluginBase.__init__(self)

self.capacity = None # capacity value of the knapsack
self.penaltyFactor = 1.0 # penalty factor that is used when the capacity constraint is not satisfied
self.outcome = None # ID of the variable which indicates if the chosen elements satisfy the capacity constraint
self.choiceValue = None # ID of the variable which indicates the sum of the values of the chosen project elements

def _readMoreXML(self, container, xmlNode):
"""
Method to read the portion of the XML that belongs to the BaseKnapsack model
@ In, container, object, self-like object where all the variables can be stored
@ In, xmlNode, xml.etree.ElementTree.Element, XML node that needs to be read
@ Out, None
"""
container.mapping = {}

for child in xmlNode:
if child.tag == 'capacity':
self.capacity = float(child.text.strip())
elif child.tag == 'penaltyFactor':
self.penaltyFactor = float(child.text.strip())
elif child.tag == 'outcome':
self.outcome = child.text.strip()
elif child.tag == 'choiceValue':
self.choiceValue = child.text.strip()
elif child.tag == 'map':
container.mapping[child.text.strip()] = [child.get('value'),child.get('cost')]
elif child.tag == 'variables':
variables = [str(var.strip()) for var in child.text.split(",")]
else:
raise IOError("BaseKnapsackModel: xml node " + str (child.tag) + " is not allowed")


def initialize(self, container, runInfoDict, inputFiles):
"""
Method to initialize the BaseKnapsack model
@ In, container, object, self-like object where all the variables can be stored
@ In, runInfoDict, dict, dictionary containing all the RunInfo parameters (XML node <RunInfo>)
@ In, inputFiles, list, list of input files (if any)
@ Out, None
"""
pass


def run(self, container, inputDict):
"""
This method calculates the sum of the chosen element values and check if the capacity constraint
is satisfied
@ In, container, object, self-like object where all the variables can be stored
@ In, inputDict, dict, dictionary of inputs from RAVEN
"""
totalValue = 0.0

for key in container.mapping:
if key in inputDict.keys() and inputDict[key] in [0.0,1.0]:
if inputDict[key] == 1.0:
testValue = self.capacity - inputDict[container.mapping[key][1]]
if testValue >= 0:
self.capacity = self.capacity - inputDict[container.mapping[key][1]]
totalValue = totalValue + inputDict[container.mapping[key][0]]
else:
self.capacity = self.capacity - inputDict[container.mapping[key][1]]
totalValue = totalValue - inputDict[container.mapping[key][0]] * self.penaltyFactor
elif inputDict[key] == 0.0:
pass
else:
raise IOError("BaseKnapsackModel: variable " + str(key) + " does not have a 0/1 value.")
else:
raise IOError("BaseKnapsackModel: variable " + str(key) + " is not found in the set of input variables.")

if self.capacity>=0:
container.__dict__[self.outcome] = 0.
else:
container.__dict__[self.outcome] = 1.

container.__dict__[self.choiceValue] = totalValue
11 changes: 11 additions & 0 deletions tests/gold/BaseKnapsack/PrintPS.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
element1Status,element2Status,element3Status,element4Status,element5Status,element1Val,element2Val,element3Val,element4Val,element5Val,element1Cost,element2Cost,element3Cost,element4Cost,element5Cost,validity,totalValue
1.0,0.0,1.0,1.0,1.0,1,2,3,4,5,1,1,1,1,1,0.0,13.0
1.0,1.0,1.0,0.0,0.0,1,2,3,4,5,1,1,1,1,1,0.0,6.0
0.0,1.0,1.0,1.0,1.0,1,2,3,4,5,1,1,1,1,1,1.0,4.0
1.0,0.0,1.0,1.0,1.0,1,2,3,4,5,1,1,1,1,1,1.0,-13.0
0.0,0.0,1.0,1.0,1.0,1,2,3,4,5,1,1,1,1,1,1.0,-12.0
1.0,1.0,0.0,1.0,1.0,1,2,3,4,5,1,1,1,1,1,1.0,-12.0
1.0,0.0,0.0,0.0,0.0,1,2,3,4,5,1,1,1,1,1,1.0,-1.0
1.0,1.0,1.0,1.0,1.0,1,2,3,4,5,1,1,1,1,1,1.0,-15.0
0.0,0.0,1.0,1.0,0.0,1,2,3,4,5,1,1,1,1,1,1.0,-7.0
1.0,1.0,0.0,0.0,1.0,1,2,3,4,5,1,1,1,1,1,1.0,-8.0
110 changes: 110 additions & 0 deletions tests/test_BaseKnapsackModel.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<Simulation verbosity="debug">
<TestInfo>
<name>framework.BaseKnapsack</name>
<author>mandd</author>
<created>2021-02-02</created>
<classesTested>Models.ExternalModel.LOGOS.BaseKnapsackModel</classesTested>
<description>
This test is aimed to test the BaseKnapsackModel class
</description>
</TestInfo>

<RunInfo>
<WorkingDir>BaseKnapsack</WorkingDir>
<Sequence>Run</Sequence>
<batchSize>1</batchSize>
</RunInfo>

<Models>
<ExternalModel name="knapsack" subType="LOGOS.BaseKnapsackModel">
<variables>element1Status,element2Status,element3Status,element4Status,element5Status,
element1Val,element2Val,element3Val,element4Val,element5Val,
element1Cost,element2Cost,element3Cost,element4Cost,element5Cost,
validity,totalValue</variables>
<capacity>10</capacity>
<penaltyFactor>1.</penaltyFactor>
<outcome>validity</outcome>
<choiceValue>totalValue</choiceValue>
<map value='element1Val' cost='element1Cost' >element1Status</map>
<map value='element2Val' cost='element2Cost' >element2Status</map>
<map value='element3Val' cost='element3Cost' >element3Status</map>
<map value='element4Val' cost='element4Cost' >element4Status</map>
<map value='element5Val' cost='element5Cost' >element5Status</map>
</ExternalModel>
</Models>

<Distributions>
<Bernoulli name="bernDistrib">
<p>0.5</p>
</Bernoulli>
</Distributions>

<Samplers>
<MonteCarlo name="MC_external">
<samplerInit>
<limit>10</limit>
</samplerInit>
<variable name="element1Status">
<distribution>bernDistrib</distribution>
</variable>
<variable name="element2Status">
<distribution>bernDistrib</distribution>
</variable>
<variable name="element3Status">
<distribution>bernDistrib</distribution>
</variable>
<variable name="element4Status">
<distribution>bernDistrib</distribution>
</variable>
<variable name="element5Status">
<distribution>bernDistrib</distribution>
</variable>

<constant name="element1Val">1</constant>
<constant name="element2Val">2</constant>
<constant name="element3Val">3</constant>
<constant name="element4Val">4</constant>
<constant name="element5Val">5</constant>

<constant name="element1Cost">1</constant>
<constant name="element2Cost">1</constant>
<constant name="element3Cost">1</constant>
<constant name="element4Cost">1</constant>
<constant name="element5Cost">1</constant>
</MonteCarlo>
</Samplers>

<Steps>
<MultiRun name="Run">
<Input class="DataObjects" type="PointSet" >dummyPS</Input>
<Model class="Models" type="ExternalModel" >knapsack</Model>
<Sampler class="Samplers" type="MonteCarlo" >MC_external</Sampler>
<Output class="DataObjects" type="PointSet" >dataPS</Output>
<Output class="OutStreams" type="Print" >PrintPS</Output>
</MultiRun>
</Steps>

<OutStreams>
<Print name="PrintPS">
<type>csv</type>
<source>dataPS</source>
<what>input,output</what>
</Print>
</OutStreams>

<DataObjects>
<PointSet name="dummyPS">
<Input>element1Status,element2Status,element3Status,element4Status,element5Status,
element1Val,element2Val,element3Val,element4Val,element5Val,
element1Cost,element2Cost,element3Cost,element4Cost,element5Cost</Input>
<Output>OutputPlaceHolder</Output>
</PointSet>
<PointSet name="dataPS">
<Input>element1Status,element2Status,element3Status,element4Status,element5Status,
element1Val,element2Val,element3Val,element4Val,element5Val,
element1Cost,element2Cost,element3Cost,element4Cost,element5Cost</Input>
<Output>validity,totalValue</Output>
</PointSet>
</DataObjects>

</Simulation>
6 changes: 6 additions & 0 deletions tests/tests
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,10 @@
UnorderedCsv = 'IncrementalNPV_options/dumpCostOutput.csv'
[../]

[./BaseKnapsack]
type = 'RavenFramework'
input = 'test_BaseKnapsackModel.xml'
UnorderedCsv = 'BaseKnapsack/PrintPS.csv'
[../]

[]

0 comments on commit cc134f3

Please sign in to comment.