diff --git a/.gitignore b/.gitignore index 3ed774879974db6733af7ce0ba5f2431cd856517..e89eb583c2b427fc0e033303043c1881c0d22bd1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,12 @@ +<<<<<<< HEAD +output +.settings +.classpath +.project + +/bin/ +/target/ +======= .settings .classpath .project @@ -92,3 +101,4 @@ target .project org.* /src/main/java/miscellaneous/Essai.java +>>>>>>> master diff --git a/data/input/fishTryRealBV_CC.xml b/data/input/fishTryRealBV_CC.xml index e5224a8d6ae123952b942bec9a2aee70fb036ff3..f78f02ec24b7201d261362d5b26b25947499a884 100644 --- a/data/input/fishTryRealBV_CC.xml +++ b/data/input/fishTryRealBV_CC.xml @@ -1,124 +1,366 @@ <list> - <species.DiadromousFishGroup> - <name>species A</name> - <color> - <red>255</red> - <green>0</green> - <blue>0</blue> - <alpha>255</alpha> - </color> - <linfVonBert>60.0</linfVonBert> - <dMaxDisp>0.0</dMaxDisp> - <lFirstMaturity>40.0</lFirstMaturity> - <fileNameInputForInitialObservation>data/input/reality/Obs1900.csv</fileNameInputForInitialObservation> + <species.DiadromousFishGroup> + <name>species A</name> + <color> + <red>255</red> + <green>0</green> + <blue>0</blue> + <alpha>255</alpha> + </color> - <processes> - <processesAtBegin> - <species.PopulateBasinNetworkWithANorthLimit> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - <nbSIPerBasin>200</nbSIPerBasin> - <initialLength>2.0</initialLength> - <nbFishPerSI>2500</nbFishPerSI> - <northLimitLatitude>43.54</northLimitLatitude> - </species.PopulateBasinNetworkWithANorthLimit> - </processesAtBegin> + <dMaxDisp>300.0</dMaxDisp> - <processesEachStep> - <environment.InformTime> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - </environment.InformTime> - <species.PlopProcess> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - <temporisation>0</temporisation> - </species.PlopProcess> + <nutrientRoutine> + <nutrientsOfInterest> + <string>N</string> + <string>P</string> + </nutrientsOfInterest> + <residenceTime>30.0</residenceTime> + <excretionRate class="hashtable"> + <entry> + <string>P</string> + <double>2.17E-6</double> + </entry> + <entry> + <string>N</string> + <double>2.471E-5</double> + </entry> + </excretionRate> + <fishFeaturesPreSpawning class="hashtable"> + <entry> + <species.DiadromousFish_-Gender>MALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>bLW_Gonad</string> + <double>3.3838</double> + </entry> + <entry> + <string>aLW_Gonad</string> + <double>-8.8744</double> + </entry> + <entry> + <string>bLW</string> + <double>3.2252</double> + </entry> + <entry> + <string>aLW</string> + <double>0.004095817237891344</double> + </entry> + </hashtable> + </entry> + <entry> + <species.DiadromousFish_-Gender>FEMALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>bLW_Gonad</string> + <double>2.6729</double> + </entry> + <entry> + <string>aLW_Gonad</string> + <double>-5.2425</double> + </entry> + <entry> + <string>bLW</string> + <double>3.3429</double> + </entry> + <entry> + <string>aLW</string> + <double>0.002665367811305362</double> + </entry> + </hashtable> + </entry> + </fishFeaturesPreSpawning> + <fishFeaturesPostSpawning class="hashtable"> + <entry> + <species.DiadromousFish_-Gender>MALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>bLW_Gonad</string> + <double>3.8331</double> + </entry> + <entry> + <string>aLW_Gonad</string> + <double>-11.285</double> + </entry> + <entry> + <string>bLW</string> + <double>2.9973</double> + </entry> + <entry> + <string>aLW</string> + <double>0.010383887012522573</double> + </entry> + </hashtable> + </entry> + <entry> + <species.DiadromousFish_-Gender>FEMALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>bLW_Gonad</string> + <double>2.8545</double> + </entry> + <entry> + <string>aLW_Gonad</string> + <double>-6.6234</double> + </entry> + <entry> + <string>bLW</string> + <double>2.9418</double> + </entry> + <entry> + <string>aLW</string> + <double>0.013199187556948952</double> + </entry> + </hashtable> + </entry> + </fishFeaturesPostSpawning> + <juvenileFeatures class="hashtable"> + <entry> + <string>bLW</string> + <double>3.0306</double> + </entry> + <entry> + <string>aLW</string> + <double>0.006986429759979109</double> + </entry> + </juvenileFeatures> + <compoCarcassPreSpawning class="hashtable"> + <entry> + <species.DiadromousFish_-Gender>MALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.00666</double> + </entry> + <entry> + <string>N</string> + <double>0.02941</double> + </entry> + </hashtable> + </entry> + <entry> + <species.DiadromousFish_-Gender>FEMALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.006730000000000001</double> + </entry> + <entry> + <string>N</string> + <double>0.029580000000000002</double> + </entry> + </hashtable> + </entry> + </compoCarcassPreSpawning> + <compoCarcassPostSpawning class="hashtable"> + <entry> + <species.DiadromousFish_-Gender>MALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.00961</double> + </entry> + <entry> + <string>N</string> + <double>0.0279</double> + </entry> + </hashtable> + </entry> + <entry> + <species.DiadromousFish_-Gender>FEMALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.00997</double> + </entry> + <entry> + <string>N</string> + <double>0.03216</double> + </entry> + </hashtable> + </entry> + </compoCarcassPostSpawning> + <compoGametes class="hashtable"> + <entry> + <species.DiadromousFish_-Gender>MALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.00724</double> + </entry> + <entry> + <string>N</string> + <double>0.0325</double> + </entry> + </hashtable> + </entry> + <entry> + <species.DiadromousFish_-Gender>FEMALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.0032</double> + </entry> + <entry> + <string>N</string> + <double>0.03242</double> + </entry> + </hashtable> + </entry> + </compoGametes> + <compoJuvenile class="hashtable"> + <entry> + <string>P</string> + <double>0.00887</double> + </entry> + <entry> + <string>N</string> + <double>0.02803</double> + </entry> + </compoJuvenile> + </nutrientRoutine> - <species.Age> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - </species.Age> + <fileNameInputForInitialObservation>data/input/reality/Obs1900.csv</fileNameInputForInitialObservation> + <centileForRange>0.95</centileForRange> + <parameterSetfileName>data/input/reality/parameterSet.csv</parameterSetfileName> + <parameterSetLine>0</parameterSetLine> + <yearOfTheUpdate>0</yearOfTheUpdate> + <basinsToUpdateFile>data/input/reality/basinsToUpdate.csv</basinsToUpdateFile> + <outputPath>data/output/</outputPath> + <fileNameFluxes>nutrientFluxes</fileNameFluxes> + <lengthAtHatching>2.0</lengthAtHatching> + <linfVonBertForFemale>70.0</linfVonBertForFemale> + <linfVonBertForMale>70.0</linfVonBertForMale> + <lFirstMaturityForFemale>55.0</lFirstMaturityForFemale> + <lFirstMaturityForMale>40.0</lFirstMaturityForMale> + <processes> + <processesAtBegin> + <species.PopulateBasinNetwork> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <nbSIPerBasin>200</nbSIPerBasin> + <initialLength>20.0</initialLength> + <nbFishPerSI>2500</nbFishPerSI> + </species.PopulateBasinNetwork> + </processesAtBegin> - <species.Grow> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - <tempMinGrow>3.0</tempMinGrow> - <tempMaxGrow>26.0</tempMaxGrow> - <tempOptGrow>17.0</tempOptGrow> - <kOpt>0.3900707</kOpt> - <sigmaDeltaLVonBert>0.2</sigmaDeltaLVonBert> - </species.Grow> + <processesEachStep> + <environment.InformTime> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + </environment.InformTime> + <species.PlopProcess> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <temporisation>0</temporisation> + </species.PlopProcess> - <species.DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - <alpha0Rep>-2.9</alpha0Rep> - <alpha1Rep>19.7</alpha1Rep> - <alpha3Rep>0.0</alpha3Rep> - <meanBvSurface>17351</meanBvSurface> - <standardDeviationBvSurface>35594</standardDeviationBvSurface> - <meanInterDistance>300.0</meanInterDistance> - <standardDeviationInterDistance>978.0 - </standardDeviationInterDistance> - <pHomingForReachEquil>0.75</pHomingForReachEquil> - <pHomingAfterEquil>0.75</pHomingAfterEquil> - <NbYearForInstallPop>0</NbYearForInstallPop> - <riverMigrationSeason>SPRING</riverMigrationSeason> - <alpha2Rep>0.0</alpha2Rep> - <meanSpawnersLengthAtRepro>45.0</meanSpawnersLengthAtRepro> - <standardDeviationOfSpawnersLengthAtRepro>2.0 - </standardDeviationOfSpawnersLengthAtRepro> - <weightOfDeathBasin>0.4</weightOfDeathBasin> - </species.DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin> + <species.Age> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + </species.Age> - <species.Survive> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - <tempMinMortGenInRiv>10.0</tempMinMortGenInRiv> - <tempMaxMortGenInRiv>23.0</tempMaxMortGenInRiv> - <tempOptMortGenInRiv>20.0</tempOptMortGenInRiv> - <survivalProbOptGenInRiv>1.0</survivalProbOptGenInRiv> - <mortalityRateInRiver>0.4</mortalityRateInRiver> - <mortalityRateInSea>0.4</mortalityRateInSea> - <mortalityRateInOffshore>0.4</mortalityRateInOffshore> - </species.Survive> + <species.Grow> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <tempMinGrow>3.0</tempMinGrow> + <tempMaxGrow>26.0</tempMaxGrow> + <tempOptGrow>17.0</tempOptGrow> + <kOptForFemale>0.20323011454056628</kOptForFemale> + <kOptForMale>0.3004992503785338</kOptForMale> + <sigmaDeltaLVonBert>0.2</sigmaDeltaLVonBert> + </species.Grow> - <species.ReproduceAndSurviveAfterReproduction> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - <reproductionSeason>SPRING</reproductionSeason> - <tempMinRep>9.846984</tempMinRep> - <tempMaxRep>26.0</tempMaxRep> - <tempOptRep>20.0</tempOptRep> - <eta>2.4</eta> - <ratioS95__S50>1.9</ratioS95__S50> - <a>135000.0</a> - <delta__t>0.33</delta__t> - <survOptRep>0.0017</survOptRep> - <lambda>4.1E-4</lambda> - <initialLength>2.0</initialLength> - <sigmaRecruitment>0.2</sigmaRecruitment> - <survivalRateAfterReproduction>0.1</survivalRateAfterReproduction> - <maxNumberOfSuperIndividualPerReproduction>50.0 - </maxNumberOfSuperIndividualPerReproduction> - </species.ReproduceAndSurviveAfterReproduction> + <species.DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <alpha0Rep>-2.9</alpha0Rep> + <alpha1Rep>19.7</alpha1Rep> + <alpha3Rep>0.0</alpha3Rep> + <meanBvSurface>17351</meanBvSurface> + <standardDeviationBvSurface>35594</standardDeviationBvSurface> + <meanInterDistance>300.0</meanInterDistance> + <standardDeviationInterDistance>978.0</standardDeviationInterDistance> + <pHomingForReachEquil>0.75</pHomingForReachEquil> + <pHomingAfterEquil>0.75</pHomingAfterEquil> + <NbYearForInstallPop>0</NbYearForInstallPop> + <riverMigrationSeason>SPRING</riverMigrationSeason> + <alpha2Rep>0.0</alpha2Rep> + <meanSpawnersLengthAtRepro>45.0</meanSpawnersLengthAtRepro> + <standardDeviationOfSpawnersLengthAtRepro>2.0</standardDeviationOfSpawnersLengthAtRepro> + <weightOfDeathBasin>0.4</weightOfDeathBasin> + </species.DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin> - <species.MigrateToSea> - <seaMigrationSeason>SUMMER</seaMigrationSeason> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - </species.MigrateToSea> + <species.Survive> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <tempMinMortGenInRiv>10.0</tempMinMortGenInRiv> + <tempMaxMortGenInRiv>23.0</tempMaxMortGenInRiv> + <tempOptMortGenInRiv>20.0</tempOptMortGenInRiv> + <survivalProbOptGenInRiv>1.0</survivalProbOptGenInRiv> + <mortalityRateInRiver>0.4</mortalityRateInRiver> + <mortalityRateInSea>0.4</mortalityRateInSea> + <mortalityRateInOffshore>0.4</mortalityRateInOffshore> + </species.Survive> - <environment.updateTemperatureInRealBasin> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - <offshoreTemperature>12.0</offshoreTemperature> - </environment.updateTemperatureInRealBasin> - </processesEachStep> + <species.ExportFluxes> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <exportSeason>SPRING</exportSeason> + <fileNameOutput>EffectiveFluxes</fileNameOutput> + </species.ExportFluxes> - <processesAtEnd> - <species.IdentifyPopulation> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - </species.IdentifyPopulation> - <species.TypeTrajectoryCV> - <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - <fileNameOutput>data/output/JeuParam100_2100RCP85_A - </fileNameOutput> - </species.TypeTrajectoryCV> + <species.ExportBiomass> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <survivalRateAfterReproduction>0.1</survivalRateAfterReproduction> + <exportSeason>SPRING</exportSeason> + <fileNameOutput>BiomassFluxes</fileNameOutput> + </species.ExportBiomass> - </processesAtEnd> - </processes> - <useCemetery>false</useCemetery> - </species.DiadromousFishGroup> -</list> \ No newline at end of file + <species.ReproduceAndSurviveAfterReproductionWithDiagnose> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <reproductionSeason>SPRING</reproductionSeason> + <tempMinRep>10.443039492695249</tempMinRep> + <tempMaxRep>26.0</tempMaxRep> + <tempOptRep>20.0</tempOptRep> + <eta>2.4</eta> + <ratioS95__S50>1.9</ratioS95__S50> + <a>135000.0</a> + <delta__t>0.33</delta__t> + <survOptRep>0.0017</survOptRep> + <lambda>4.1E-4</lambda> + <proportionOfFemaleAtBirth>0.5</proportionOfFemaleAtBirth> + <initialLength>2.0</initialLength> + <sigmaRecruitment>0.2</sigmaRecruitment> + <survivalRateAfterReproduction>0.1</survivalRateAfterReproduction> + <maxNumberOfSuperIndividualPerReproduction>50.0 + </maxNumberOfSuperIndividualPerReproduction> + <withDiagnose>false</withDiagnose> + <displayFluxesOnConsole>true</displayFluxesOnConsole> + </species.ReproduceAndSurviveAfterReproductionWithDiagnose> + + <species.MigrateToSea> + <seaMigrationSeason>SUMMER</seaMigrationSeason> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <displayFluxesOnConsole>true</displayFluxesOnConsole> + </species.MigrateToSea> + + <environment.updateTemperatureInRealBasin> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <offshoreTemperature>12.0</offshoreTemperature> + </environment.updateTemperatureInRealBasin> + </processesEachStep> + + <processesAtEnd> + <species.IdentifyPopulation> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <consoleDisplay>false</consoleDisplay> + <fluxesSeason>SPRING</fluxesSeason> + <years> + <long>2000</long> + <long>2100</long> + </years> + <fileNameOutput>JeuParam100_2100RCP85_A_essai</fileNameOutput> + </species.IdentifyPopulation> + <species.TypeTrajectoryCV> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <fileNameOutput>JeuParam100_2100RCP85_A_essai</fileNameOutput> + </species.TypeTrajectoryCV> + + </processesAtEnd> + </processes> + <useCemetery>false</useCemetery> + </species.DiadromousFishGroup> +</list> diff --git a/data/input/fishTryRealBV_PL.xml b/data/input/fishTryRealBV_PL.xml index 34f20e8d6b441296185b1d284227611b6982d5a5..4f3b80d4a556edc2cff3150c8bc1ecefa293092a 100644 --- a/data/input/fishTryRealBV_PL.xml +++ b/data/input/fishTryRealBV_PL.xml @@ -127,7 +127,7 @@ <long>2100</long> </years> <fluxesSeason>SPRING</fluxesSeason> - <fileNameOutput>fluxes</fileNameOutput> + <fileNameOutput>JeuParam100_20100RCP85_A_essai</fileNameOutput> </species.IdentifyPopulation> <species.ExportRecruitment> @@ -144,7 +144,7 @@ <species.ExportPopulationStatus> <synchronisationMode>ASYNCHRONOUS</synchronisationMode> - <fileNameMortality>mortalty</fileNameMortality> + <fileNameMortality>mortality</fileNameMortality> <fileNameMortalityCrash>mortalityCrash</fileNameMortalityCrash> <fileNameStockTrap>stockTrap</fileNameStockTrap> <fileNamePopulationStatus>populationStatus</fileNamePopulationStatus> diff --git a/data/input/fishTryRealBV_calibration.xml b/data/input/fishTryRealBV_calibration.xml new file mode 100644 index 0000000000000000000000000000000000000000..3a4317408bf110edbf08c9104bf97ef6375eee9a --- /dev/null +++ b/data/input/fishTryRealBV_calibration.xml @@ -0,0 +1,352 @@ +<list> + <species.DiadromousFishGroup> + <name>species A</name> + <color> + <red>255</red> + <green>0</green> + <blue>0</blue> + <alpha>255</alpha> + </color> + <dMaxDisp>300.0</dMaxDisp> + <linfVonBertForFemale>70.0</linfVonBertForFemale> + <linfVonBertForMale>70.0</linfVonBertForMale> + <lFirstMaturityForFemale>55.0</lFirstMaturityForFemale> + <lFirstMaturityForMale>40.0</lFirstMaturityForMale> + <lengthAtHatching>2</lengthAtHatching> + <nutrientRoutine> + <nutrientsOfInterest> + <string>N</string> + <string>P</string> + </nutrientsOfInterest> + <residenceTime>30.0</residenceTime> + <excretionRate class="hashtable"> + <entry> + <string>P</string> + <double>2.17E-6</double> + </entry> + <entry> + <string>N</string> + <double>2.471E-5</double> + </entry> + </excretionRate> + <fishFeaturesPreSpawning class="hashtable"> + <entry> + <species.DiadromousFish_-Gender>MALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>bLW_Gonad</string> + <double>3.3838</double> + </entry> + <entry> + <string>aLW_Gonad</string> + <double>-8.8744</double> + </entry> + <entry> + <string>bLW</string> + <double>2.1774</double> + </entry> + <entry> + <string>aLW</string> + <double>0.27144384321211973</double> + </entry> + </hashtable> + </entry> + <entry> + <species.DiadromousFish_-Gender>FEMALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>bLW_Gonad</string> + <double>2.6729</double> + </entry> + <entry> + <string>aLW_Gonad</string> + <double>-5.2425</double> + </entry> + <entry> + <string>bLW</string> + <double>3.147</double> + </entry> + <entry> + <string>aLW</string> + <double>0.007388725660209693</double> + </entry> + </hashtable> + </entry> + </fishFeaturesPreSpawning> + <fishFeaturesPostSpawning class="hashtable"> + <entry> + <species.DiadromousFish_-Gender>MALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>bLW_Gonad</string> + <double>3.8331</double> + </entry> + <entry> + <string>aLW_Gonad</string> + <double>-11.285</double> + </entry> + <entry> + <string>bLW</string> + <double>2.9973</double> + </entry> + <entry> + <string>aLW</string> + <double>0.010383887012522573</double> + </entry> + </hashtable> + </entry> + <entry> + <species.DiadromousFish_-Gender>FEMALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>bLW_Gonad</string> + <double>2.8545</double> + </entry> + <entry> + <string>aLW_Gonad</string> + <double>-6.6234</double> + </entry> + <entry> + <string>bLW</string> + <double>2.9418</double> + </entry> + <entry> + <string>aLW</string> + <double>0.013199187556948952</double> + </entry> + </hashtable> + </entry> + </fishFeaturesPostSpawning> + <juvenileFeatures class="hashtable"> + <entry> + <string>bLW</string> + <double>3.0306</double> + </entry> + <entry> + <string>aLW</string> + <double>0.006986429759979109</double> + </entry> + </juvenileFeatures> + <compoCarcassPreSpawning class="hashtable"> + <entry> + <species.DiadromousFish_-Gender>MALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.00666</double> + </entry> + <entry> + <string>N</string> + <double>0.02941</double> + </entry> + </hashtable> + </entry> + <entry> + <species.DiadromousFish_-Gender>FEMALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.006730000000000001</double> + </entry> + <entry> + <string>N</string> + <double>0.029580000000000002</double> + </entry> + </hashtable> + </entry> + </compoCarcassPreSpawning> + <compoCarcassPostSpawning class="hashtable"> + <entry> + <species.DiadromousFish_-Gender>MALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.00961</double> + </entry> + <entry> + <string>N</string> + <double>0.0279</double> + </entry> + </hashtable> + </entry> + <entry> + <species.DiadromousFish_-Gender>FEMALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.00997</double> + </entry> + <entry> + <string>N</string> + <double>0.03216</double> + </entry> + </hashtable> + </entry> + </compoCarcassPostSpawning> + <compoGametes class="hashtable"> + <entry> + <species.DiadromousFish_-Gender>MALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.00724</double> + </entry> + <entry> + <string>N</string> + <double>0.0325</double> + </entry> + </hashtable> + </entry> + <entry> + <species.DiadromousFish_-Gender>FEMALE</species.DiadromousFish_-Gender> + <hashtable> + <entry> + <string>P</string> + <double>0.0032</double> + </entry> + <entry> + <string>N</string> + <double>0.03242</double> + </entry> + </hashtable> + </entry> + </compoGametes> + <compoJuvenile class="hashtable"> + <entry> + <string>P</string> + <double>0.00887</double> + </entry> + <entry> + <string>N</string> + <double>0.02803</double> + </entry> + </compoJuvenile> + </nutrientRoutine> + + <fileNameInputForInitialObservation>data/input/reality/Obs1900.csv</fileNameInputForInitialObservation> + <centileForRange>0.95</centileForRange> + <parameterSetfileName>data/input/reality/parameterSet.csv</parameterSetfileName> + <parameterSetLine>0</parameterSetLine> + <yearOfTheUpdate>0</yearOfTheUpdate> + <basinsToUpdateFile>data/input/reality/basinsToUpdate.csv</basinsToUpdateFile> + <outputPath>data/output/</outputPath> + <processes> + <processesAtBegin> + <species.PopulateBasinNetworkWithANorthLimit> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <nbSIPerBasin>200</nbSIPerBasin> + <initialLength>20.0</initialLength> + <nbFishPerSI>2500</nbFishPerSI> + <northLimitLatitude>43.54</northLimitLatitude> + </species.PopulateBasinNetworkWithANorthLimit> + </processesAtBegin> + + <processesEachStep> + <environment.InformTime> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + </environment.InformTime> + + <species.PlopProcess> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <temporisation>0</temporisation> + </species.PlopProcess> + + <species.Age> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + </species.Age> + + <!-- 3 --> + <species.Grow> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <tempMinGrow>3.0</tempMinGrow> + <tempMaxGrow>26.0</tempMaxGrow> + <tempOptGrow>17.0</tempOptGrow> + <kOptForFemale>0.5363472</kOptForFemale> + <kOptForMale>0.3900707</kOptForMale> + <sigmaDeltaLVonBert>0.2</sigmaDeltaLVonBert> + </species.Grow> + + <species.DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <alpha0Rep>-2.9</alpha0Rep> + <alpha1Rep>19.7</alpha1Rep> + <alpha3Rep>0.0</alpha3Rep> + <meanBvSurface>17351</meanBvSurface> + <standardDeviationBvSurface>35594</standardDeviationBvSurface> + <meanInterDistance>300.0</meanInterDistance> + <standardDeviationInterDistance>978.0</standardDeviationInterDistance> + <pHomingForReachEquil>0.75</pHomingForReachEquil> + <pHomingAfterEquil>0.75</pHomingAfterEquil> + <NbYearForInstallPop>0</NbYearForInstallPop> + <riverMigrationSeason>SPRING</riverMigrationSeason> + <alpha2Rep>0.0</alpha2Rep> + <meanSpawnersLengthAtRepro>45.0</meanSpawnersLengthAtRepro> + <standardDeviationOfSpawnersLengthAtRepro>2.0</standardDeviationOfSpawnersLengthAtRepro> + <weightOfDeathBasin>0.4</weightOfDeathBasin> + </species.DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin> + + <species.Survive> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <tempMinMortGenInRiv>10.0</tempMinMortGenInRiv> + <tempMaxMortGenInRiv>23.0</tempMaxMortGenInRiv> + <tempOptMortGenInRiv>20.0</tempOptMortGenInRiv> + <survivalProbOptGenInRiv>1.0</survivalProbOptGenInRiv> + <mortalityRateInRiver>0.4</mortalityRateInRiver> + <mortalityRateInSea>0.4</mortalityRateInSea> + <mortalityRateInOffshore>0.4</mortalityRateInOffshore> + </species.Survive> + + <!-- 6 --> + <species.ReproduceAndSurviveAfterReproductionWithDiagnose> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <reproductionSeason>SPRING</reproductionSeason> + <tempMinRep>9.846984</tempMinRep> + <tempMaxRep>26.0</tempMaxRep> + <tempOptRep>20.0</tempOptRep> + <eta>2.4</eta> + <ratioS95__S50>1.9</ratioS95__S50> + <a>135000.0</a> + <delta__t>0.33</delta__t> + <survOptRep>0.0017</survOptRep> + <lambda>4.1E-4</lambda> + <proportionOfFemaleAtBirth>0.5</proportionOfFemaleAtBirth> + <initialLength>2.0</initialLength> + <sigmaRecruitment>0.2</sigmaRecruitment> + <survivalRateAfterReproduction>0.1</survivalRateAfterReproduction> + <maxNumberOfSuperIndividualPerReproduction>50.0 + </maxNumberOfSuperIndividualPerReproduction> + <withDiagnose>false</withDiagnose> + </species.ReproduceAndSurviveAfterReproductionWithDiagnose> + + <species.MigrateToSea> + <seaMigrationSeason>SUMMER</seaMigrationSeason> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + </species.MigrateToSea> + + <environment.updateTemperatureInRealBasin> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <offshoreTemperature>12.0</offshoreTemperature> + </environment.updateTemperatureInRealBasin> + </processesEachStep> + + <processesAtEnd> + <species.IdentifyPopulation> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <consoleDisplay>false</consoleDisplay> + <fluxesSeason>SPRING</fluxesSeason> + <years> + <long>2000</long> + <long>2100</long> + </years> + <fileNameOutput>fluxes</fileNameOutput> + </species.IdentifyPopulation> + <species.TypeTrajectoryCV> + <synchronisationMode>ASYNCHRONOUS</synchronisationMode> + <fileNameOutput>JeuParam100_2100RCP85_A_essai</fileNameOutput> + </species.TypeTrajectoryCV> + + </processesAtEnd> + </processes> + <useCemetery>false</useCemetery> + </species.DiadromousFishGroup> +</list> diff --git a/data/input/obsTryReal.xml b/data/input/obsTryReal.xml index 40e61b0e083ff332e45968f4d3333d4c06aaa2e9..651c6ae2334017a349b70749108bbe99ae6fe920 100644 --- a/data/input/obsTryReal.xml +++ b/data/input/obsTryReal.xml @@ -38,6 +38,55 @@ <variableName>getRangeDistributionWithLat</variableName> </miscellaneous.TemporalRangeSerieChart> + <fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + <graphType>LINE</graphType> + <title>Mean age at first reproduction for female</title> + <xAxisLabel>Time (season)</xAxisLabel> + <yAxisLabel>age (year)</yAxisLabel> + <variableName>getMeanAgeOfFirstReprodutionForFemale</variableName> + </fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + + <fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + <graphType>LINE</graphType> + <title>Mean age at first reproduction for male</title> + <xAxisLabel>Time (season)</xAxisLabel> + <yAxisLabel>age (year)</yAxisLabel> + <variableName>getMeanAgeOfFirstReprodutionForMale</variableName> + </fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + + + <fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + <graphType>LINE</graphType> + <title>Statistic for male spawners age</title> + <xAxisLabel>Time (season)</xAxisLabel> + <yAxisLabel>age (year)</yAxisLabel> + <variableName>computeMaleSpawnerForFirstTimeSummaryStatistic</variableName> + </fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + + <fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + <graphType>LINE</graphType> + <title>Statistic for female spawners age</title> + <xAxisLabel>Time (season)</xAxisLabel> + <yAxisLabel>age (year)</yAxisLabel> + <variableName>computeFemaleSpawnerForFirstTimeSummaryStatistic</variableName> + </fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + + <fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + <graphType>LINE</graphType> + <title>Mean length at first reproduction for female</title> + <xAxisLabel>Time (season)</xAxisLabel> + <yAxisLabel>length (cm)</yAxisLabel> + <variableName>getMeanLengthOfFirstReprodutionForFemale</variableName> + </fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + + <fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + <graphType>LINE</graphType> + <title>Mean length at first reproduction for male</title> + <xAxisLabel>Time (season)</xAxisLabel> + <yAxisLabel>length (cm)</yAxisLabel> + <variableName>getMeanLengthOfFirstReprodutionForMale</variableName> + </fr.cemagref.observation.observers.jfreechart.TemporalSerieChart> + </observers> </fr.cemagref.observation.kernel.ObservablesHandler> </entry> @@ -60,7 +109,7 @@ <yAxisLabel>% Autochtone</yAxisLabel> <variableName>getLastPercentageOfAutochtone</variableName> </miscellaneous.TemporalSerieChartForBasin> - + </observers> </fr.cemagref.observation.kernel.ObservablesHandler> </entry> diff --git a/pom.xml b/pom.xml index 17accac9fea90bfbf04b61bb925e73eb413ab238..38ec16ac02a4e706d911e4755cbf6146ce61d682 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> @@ -8,14 +7,15 @@ <!-- *** POM Relationships *************************************** --> <!-- ************************************************************* --> - <groupId>fr.irstea</groupId> + <groupId>GR3D</groupId> <artifactId>GR3D</artifactId> <version>2.0-SNAPSHOT</version> <properties> <netbeans.hint.license>gpl30</netbeans.hint.license> <build.timestamp>${maven.build.timestamp}</build.timestamp> - <project.build.sourceEncoding>ISO-8859-15</project.build.sourceEncoding> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <geotools.version>21-RC</geotools.version> </properties> <dependencies> @@ -31,22 +31,58 @@ <version>2.6</version> <type>jar</type> </dependency> + <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> + + <!-- https://mvnrepository.com/artifact/org.geotools/gt-shapefile --> <dependency> <groupId>org.geotools</groupId> <artifactId>gt-shapefile</artifactId> - <version>12-RC1</version> + <version>${geotools.version}</version> + </dependency> + + <!-- https://mvnrepository.com/artifact/org.geotools/gt-swing --> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-swing</artifactId> + <version>${geotools.version}</version> + </dependency> + + <!-- https://mvnrepository.com/artifact/org.geotools/gt-render --> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-render</artifactId> + <version>${geotools.version}</version> + </dependency> + + + + <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.6</version> </dependency> + + <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-math3</artifactId> <version>3.5</version> </dependency> + + <!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream --> + <dependency> + <groupId>com.thoughtworks.xstream</groupId> + <artifactId>xstream</artifactId> + <version>1.4.11.1</version> + </dependency> + </dependencies> <!-- ************************************************************* --> @@ -75,9 +111,10 @@ <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> + <version>3.7.0</version> <configuration> - <source>1.6</source> - <target>1.6</target> + <source>1.8</source> + <target>1.8</target> </configuration> </plugin> @@ -86,13 +123,14 @@ <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <extensions>true</extensions> + <version>3.5.0</version> <executions> </executions> <configuration> + <manifestLocation>target/META-INF</manifestLocation> <!--finalName>${symbolic.name}_${project.version}</finalName --> <instructions> - <!--Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name> - <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName --> + <!--Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name> <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName --> <Import-Package>*;resolution:=optional</Import-Package> <Export-Package>fr.cemagref.simaqualife.extensions.pilot.BatchRunner;fr.cemagref.simaqualife.*;miscellaneous.*</Export-Package> <Embed-Dependency>*;scope=!provided;inline=true</Embed-Dependency> @@ -101,6 +139,7 @@ </instructions> </configuration> </plugin> + <plugin> <!-- uses the previously generated Manifest file (with maven-bundle-plugin) --> <artifactId>maven-jar-plugin</artifactId> @@ -111,8 +150,7 @@ </configuration> </plugin> <plugin> - <!-- plugin used for merging the various GeoTools META-INF/services files - (specific transformer below) --> + <!-- plugin used for merging the various GeoTools META-INF/services files (specific transformer below) --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>1.3.1</version> @@ -124,8 +162,7 @@ </goals> <configuration> <transformers> - <transformer - implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> + <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> </transformers> </configuration> </execution> @@ -152,6 +189,7 @@ </build> <repositories> + <repository> <id>trac.clermont.cemagref.fr.nexus.public</id> <url>http://trac.clermont.cemagref.fr/nexus/content/groups/public</url> @@ -160,24 +198,28 @@ <id>trac.clermont.cemagref.fr.nexus.snapshots</id> <url>http://trac.clermont.cemagref.fr/nexus/content/repositories/snapshots</url> </repository> + <repository> <id>osgeo</id> <name>Open Source Geospatial Foundation Repository</name> <url>http://download.osgeo.org/webdav/geotools/</url> </repository> + <repository> <snapshots> <enabled>true</enabled> </snapshots> - <id>opengeo</id> - <name>OpenGeo Maven Repository</name> - <url>http://repo.opengeo.org</url> + <id>boundless</id> + <name>Boundless Maven Repository</name> + <url>http://repo.boundlessgeo.com/main</url> </repository> + <repository> <id>maven2-repository.dev.java.net</id> <name>Java.net repository</name> <url>http://download.java.net/maven/2</url> </repository> + </repositories> </project> diff --git a/src/main/java/environment/BasinNetworkReal.java b/src/main/java/environment/BasinNetworkReal.java index eb5f34b76b4e0b4b91d183cfbb9cf6b766296c49..a1012096ecba75637191b330f3cc6bccc2e02680 100644 --- a/src/main/java/environment/BasinNetworkReal.java +++ b/src/main/java/environment/BasinNetworkReal.java @@ -2,7 +2,7 @@ package environment; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; -import com.vividsolutions.jts.geom.MultiPolygon; + import fr.cemagref.simaqualife.kernel.util.TransientParameters.InitTransientParameters; import fr.cemagref.simaqualife.pilot.Pilot; @@ -25,7 +25,11 @@ import miscellaneous.QueueMemoryMap; import org.geotools.data.FeatureSource; import org.geotools.data.FileDataStore; import org.geotools.data.FileDataStoreFinder; +import org.geotools.data.shapefile.ShapefileDataStore; +import org.geotools.data.simple.SimpleFeatureIterator; +import org.geotools.data.store.ContentFeatureSource; import org.geotools.feature.FeatureIterator; +import org.locationtech.jts.geom.MultiPolygon; import org.opengis.feature.simple.SimpleFeature; public class BasinNetworkReal extends BasinNetwork { @@ -84,7 +88,7 @@ public class BasinNetworkReal extends BasinNetwork { public double getSurface() { return surface; } - + public double getPDam(){ return pDam; } @@ -100,23 +104,25 @@ public class BasinNetworkReal extends BasinNetwork { private Map<String, Path2D.Double> loadBasins(String basinShpFile) { Map<String, Path2D.Double> mapBasin = new HashMap<String, Path2D.Double>(); - FileDataStore basinStore = null; - FeatureIterator<SimpleFeature> iterator = null; + ShapefileDataStore basinStore = null; + SimpleFeatureIterator iterator = null; try { - basinStore = FileDataStoreFinder.getDataStore(new File(basinShpFile)); - FeatureSource<?, SimpleFeature> featureBasin = basinStore.getFeatureSource(); - iterator = featureBasin.getFeatures().features(); + File aFile = new File(basinShpFile); + basinStore = new ShapefileDataStore(aFile.toURI().toURL()); + + ContentFeatureSource featureBasin = basinStore.getFeatureSource(); + iterator = featureBasin.getFeatures().features(); while (iterator.hasNext()) { SimpleFeature feature = iterator.next(); MultiPolygon multiPolygon = (MultiPolygon) feature.getDefaultGeometry(); // build the shape for each basin Path2D.Double shape = new Path2D.Double(); - shape.moveTo((multiPolygon.getCoordinates())[0].x, (multiPolygon.getCoordinates())[0].y); - for (int i = 1; i < multiPolygon.getCoordinates().length; i++) { - shape.lineTo((multiPolygon.getCoordinates())[i].x, (multiPolygon.getCoordinates())[i].y); - } - shape.closePath(); - mapBasin.put((String) feature.getAttribute("NOM"), shape); + shape.moveTo((multiPolygon.getCoordinates())[0].x, (multiPolygon.getCoordinates())[0].y); + for (int i = 1; i < multiPolygon.getCoordinates().length; i++) { + shape.lineTo((multiPolygon.getCoordinates())[i].x, (multiPolygon.getCoordinates())[i].y); + } + shape.closePath(); + mapBasin.put((String) feature.getAttribute("NOM"), shape); } } catch (Exception e1) { e1.printStackTrace(); @@ -124,6 +130,7 @@ public class BasinNetworkReal extends BasinNetwork { iterator.close(); basinStore.dispose(); } + return mapBasin; } @@ -136,6 +143,8 @@ public class BasinNetworkReal extends BasinNetwork { // shape files can be omitted (for batch mode) // load the shape of the seaBasin Map<String, Path2D.Double> mapSeaBasin = null; + //String cwd = System.getProperty("user.dir").concat("/"); + //System.out.println(cwd); if (seaBasinShpFile != null && seaBasinShpFile.length() > 0) { mapSeaBasin = loadBasins(seaBasinShpFile); } @@ -310,7 +319,7 @@ public class BasinNetworkReal extends BasinNetwork { public String getTemperatureCatchmentFile() { return temperatureCatchmentFile; } - + } diff --git a/src/main/java/environment/RiverBasin.java b/src/main/java/environment/RiverBasin.java index 6355d1e21ef758a5eb5afe63e88b62d626e49bec..3e26ad532736241a5c338dd154f503e21b89ebfc 100644 --- a/src/main/java/environment/RiverBasin.java +++ b/src/main/java/environment/RiverBasin.java @@ -1,6 +1,7 @@ package environment; -import org.jfree.data.time.MovingAverage; +import java.util.Hashtable; +import java.util.Map; import miscellaneous.QueueMemory; import miscellaneous.QueueMemoryMap; @@ -8,6 +9,7 @@ import fr.cemagref.observation.kernel.Observable; import fr.cemagref.observation.kernel.ObservablesHandler; import fr.cemagref.simaqualife.pilot.Pilot; import species.DiadromousFish; +import species.DiadromousFish.Gender; import species.DiadromousFishGroup; /** @@ -35,13 +37,18 @@ public class RiverBasin extends Basin { private QueueMemory<Double> lastRecsOverProdCaps; private QueueMemory<Double> lastPercentagesOfAutochtones; private QueueMemory<Double> numberOfNonNulRecruitmentDuringLastYears; // Prob of non nul recruitment during the last "memorySize" years... if 10 non nul recruitment during the last 10 year, p=0.999... if 8 non nul recruitment during the last 10 years, p = 0.8... if 0 recruitment, p = 0.001 - private QueueMemory<Double> spawnersForFirstTimeMeanAges; + private QueueMemory<Double> femaleSpawnersForFirstTimeMeanAges; + private QueueMemory<Double> maleSpawnersForFirstTimeMeanAges; + private QueueMemory<Double> femaleSpawnersForFirstTimeMeanLengths; + private QueueMemory<Double> maleSpawnersForFirstTimeMeanLengths; + + private QueueMemory<Double> numberOfNonNulRecruitmentForFinalProbOfPres; private double nativeSpawnerMortality; // mortality coefficient between recruitement and spawning for fish born in this basin private double mortalityCrash; private double stockTrap; - private double lastSpawnerNumber; + private double lastFemaleSpawnerNumber; protected static transient ObservablesHandler cobservable; @@ -62,16 +69,25 @@ public class RiverBasin extends Basin { return nbJuv; } + //TODO getSpawnerNumber(group) @Observable(description = "nb of spawners") public double getSpawnerNumber() { long nbSpawn = 0; for (DiadromousFishGroup group : this.getGroups()) { + nbSpawn += getSpawnerNumberPerGroup(group); + } + return nbSpawn; + } + + + public double getSpawnerNumberPerGroup(DiadromousFishGroup group) { + long nbSpawn = 0; + for (DiadromousFish fish : this.getFishs(group)) { if (fish.isMature()) { nbSpawn += fish.getAmount(); } } - } return nbSpawn; } @@ -98,7 +114,14 @@ public class RiverBasin extends Basin { this.lastRecsOverProdCaps = new QueueMemory<Double>(memorySize); this.lastPercentagesOfAutochtones = new QueueMemory<Double>(memorySize); this.numberOfNonNulRecruitmentDuringLastYears = new QueueMemory<Double>(memorySize); - this.spawnersForFirstTimeMeanAges = new QueueMemory<Double>(memorySize); + + + this.femaleSpawnersForFirstTimeMeanAges = new QueueMemory<Double>(memorySize) ; + this.maleSpawnersForFirstTimeMeanAges = new QueueMemory<Double>(memorySize); + + this.femaleSpawnersForFirstTimeMeanLengths = new QueueMemory<Double>(memorySize) ; + this.maleSpawnersForFirstTimeMeanLengths = new QueueMemory<Double>(memorySize) ; + this.numberOfNonNulRecruitmentForFinalProbOfPres = new QueueMemory<Double>(memorySizeLongQueue); if (cobservable == null) { @@ -189,8 +212,26 @@ public class RiverBasin extends Basin { return numberOfNonNulRecruitmentDuringLastYears; } - public QueueMemory<Double> getSpawnersForFirstTimeMeanAges() { - return spawnersForFirstTimeMeanAges; + /*public QueueMemory<Double> getFemaleSpawnersForFirstTimeMeanAges() { + return spawnersForFirstTimeMeanAges.get(Gender.FEMALE); + }*/ + + public QueueMemory<Double> getSpawnersForFirstTimeMeanLengths(DiadromousFish.Gender gender) { + if (gender == Gender.FEMALE) + return femaleSpawnersForFirstTimeMeanLengths; + else if (gender == Gender.MALE) + return maleSpawnersForFirstTimeMeanLengths; + else + return null; + } + + public QueueMemory<Double> getSpawnersForFirstTimeMeanAges(DiadromousFish.Gender gender) { + if (gender == Gender.FEMALE) + return femaleSpawnersForFirstTimeMeanAges; + else if (gender == Gender.MALE) + return maleSpawnersForFirstTimeMeanAges; + else + return null; } public QueueMemory<Double> getNumberOfNonNulRecruitmentForFinalProbOfPres(){ @@ -266,17 +307,17 @@ public class RiverBasin extends Basin { } /** - * @return the lastSpawnerNumber + * @return the last number of female spawners */ - public double getLastSpawnerNumber() { - return lastSpawnerNumber; + public double getLastFemaleSpawnerNumber() { + return lastFemaleSpawnerNumber; } /** * @param lastSpawner the lastSpawnerNumber to set */ - public void setLastSpawnerNumber(double lastSpawner) { - this.lastSpawnerNumber = lastSpawner; + public void setLastFemaleSpawnerNumber(double lastSpawner) { + this.lastFemaleSpawnerNumber = lastSpawner; } public String getPopulationStatus() { @@ -288,10 +329,10 @@ public class RiverBasin extends Basin { if (nativeSpawnerMortality> mortalityCrash) populationStatus="overZcrash"; else { - if (lastSpawnerNumber < stockTrap) + if (lastFemaleSpawnerNumber < stockTrap) populationStatus = "inTrapWithStrayers"; else { - if (lastSpawnerNumber * lastPercentagesOfAutochtones.getLastItem() < stockTrap) + if (lastFemaleSpawnerNumber * lastPercentagesOfAutochtones.getLastItem() < stockTrap) populationStatus = "inTrapWithOnlyNatives"; else populationStatus = "sustain"; diff --git a/src/main/java/environment/Time.java b/src/main/java/environment/Time.java index 6bafb52235144a3158d4732131f0a227b566bec5..a955a394bb6ce5b2c68403b5f78d26d12f64bfe0 100644 --- a/src/main/java/environment/Time.java +++ b/src/main/java/environment/Time.java @@ -24,6 +24,10 @@ public final class Time { return (long) Math.floor(time / Season.values().length); } + /** + * @return the duration of season (time step) + * @unit year + */ public static double getSeasonDuration(){ return 1./ Season.values().length; } diff --git a/src/main/java/environment/updateTemperatureInRealBasin.java b/src/main/java/environment/updateTemperatureInRealBasin.java index 747352451e8e2e972f74a75cb1a7907397f055fd..c57370bb29a970a956eabd39ab536786c06a81e0 100644 --- a/src/main/java/environment/updateTemperatureInRealBasin.java +++ b/src/main/java/environment/updateTemperatureInRealBasin.java @@ -11,9 +11,9 @@ import environment.Time.Season; import fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess; public class updateTemperatureInRealBasin extends AquaNismsGroupProcess<DiadromousFish, DiadromousFishGroup> { - + double offshoreTemperature = 12.; - + public static void main(String[] args) { System.out.println((new XStream(new DomDriver())) .toXML(new updateTemperatureInRealBasin())); } @@ -24,18 +24,23 @@ public class updateTemperatureInRealBasin extends AquaNismsGroupProcess<Diadromo if (Time.getSeason(group.getPilot()) == Season.WINTER){ Map<String, Double[]> temperaturesbasin = ((BasinNetworkReal) group.getEnvironment()).getTemperaturesBasin(Time.getYear(group.getPilot())); - for (Basin basin : group.getEnvironment().getBasins()){ - if (basin instanceof RiverBasin){ - basin.setWinterTemperature(temperaturesbasin.get(basin.getName())[0]); - basin.setSpringTemperature(temperaturesbasin.get(basin.getName())[1]); - basin.setSummerTemperature(temperaturesbasin.get(basin.getName())[2]); - basin.setFallTemperature(temperaturesbasin.get(basin.getName())[3]); - } else if (basin instanceof SeaBasin) { - basin.setWinterTemperature((offshoreTemperature + temperaturesbasin.get(group.getEnvironment().getAssociatedRiverBasin(basin).getName())[0])/2.); - basin.setSpringTemperature((offshoreTemperature + temperaturesbasin.get(group.getEnvironment().getAssociatedRiverBasin(basin).getName())[1])/2.); - basin.setSummerTemperature((offshoreTemperature + temperaturesbasin.get(group.getEnvironment().getAssociatedRiverBasin(basin).getName())[2])/2.); - basin.setFallTemperature((offshoreTemperature + temperaturesbasin.get(group.getEnvironment().getAssociatedRiverBasin(basin).getName())[3])/2.); - } + if (temperaturesbasin != null) { + for (Basin basin : group.getEnvironment().getBasins()){ + if (basin instanceof RiverBasin){ + basin.setWinterTemperature(temperaturesbasin.get(basin.getName())[0]); + basin.setSpringTemperature(temperaturesbasin.get(basin.getName())[1]); + basin.setSummerTemperature(temperaturesbasin.get(basin.getName())[2]); + basin.setFallTemperature(temperaturesbasin.get(basin.getName())[3]); + } else if (basin instanceof SeaBasin) { + basin.setWinterTemperature((offshoreTemperature + temperaturesbasin.get(group.getEnvironment().getAssociatedRiverBasin(basin).getName())[0])/2.); + basin.setSpringTemperature((offshoreTemperature + temperaturesbasin.get(group.getEnvironment().getAssociatedRiverBasin(basin).getName())[1])/2.); + basin.setSummerTemperature((offshoreTemperature + temperaturesbasin.get(group.getEnvironment().getAssociatedRiverBasin(basin).getName())[2])/2.); + basin.setFallTemperature((offshoreTemperature + temperaturesbasin.get(group.getEnvironment().getAssociatedRiverBasin(basin).getName())[3])/2.); + } + } + } + else { + System.out.println("pb with temperature at "+ Time.getYear(group.getPilot() )); } } } diff --git a/src/main/java/fr/inria/optimization/cmaes/CMAEvolutionStrategy.java b/src/main/java/fr/inria/optimization/cmaes/CMAEvolutionStrategy.java new file mode 100644 index 0000000000000000000000000000000000000000..22a5e4f7067b52951c66163d13c1b62eaab509df --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/CMAEvolutionStrategy.java @@ -0,0 +1,2706 @@ +package fr.inria.optimization.cmaes; + +import java.io.Serializable; +import java.util.*; // Properties, Arrays.sort, Formatter not needed anymore + +/* + Copyright 1996, 2003, 2005, 2007 Nikolaus Hansen + e-mail: hansen .AT. lri.fr + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License, version 3, + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + log of changes: + o updateDistribution(double[][], double[], int) introduced, + for the time being + updateDistribution(double[][], double[]) evaluates to + updateDistribution(double[][], double[], 0), but it might become + updateDistribution(double[][], double[], popsize) + o init() cannot be called twice anymore, it's saver like this + o warning() and error() print also to display-file + o checkEigenSystem() call is now an option, gives warnings, not + errors, and has its precision criteria adapted to Java. + o 06/08 fix: error for negative eigenvalues did not show up + o 09/08: diagonal option included + o updateDistribution(double[][], double[]) is available, which + implements an interface, independent of samplePopulation(). + o variable locked is set at the end of supplementRemainders, + instead at the beginning (09/03/08) + o bestever is set anew, if its current fitness is NaN (09/03/08) + o getBestRecentX() now returns really the recent best (10/03/17) + (thanks to Markus Kemmerling for reporting this problem) + o 2010/12/02: merge of r762 (diagonal option) and r2462 which were + subbranches since r752 + o test() uses flgdiag to get internally time linear + + WISH LIST: + o test and consider refinement of + updateDistribution(double[][], double[]) that + implements a "saver" interface, + independent of samplePopulation + for example updateDistribution(ISolutionPoint[] pop) + o save all input parameters as output-properties file + o explicit control of data writing behavior in terms of iterations + to wait until the next writing? + o clean up sorting of eigenvalues/vectors which is done repeatedly + o implement a good boundary handling + o check Java random number generator and/or implement a private one. + o implement a general initialize_with_evaluated_points method, which + estimates a good mean and covariance matrix either from all points + or only from the lambda best points (actually mu best points then). + cave about outlier points. + o implement a CMA-ES-specific feed points method for initialization. It should + accept a population of evaluated points iteratively. It + just needs to call updateDistribution with a population as input. + o save z instead of recomputing it? + o improve error management to reasonable standard + o provide output writing for given evaluation numbers and/or given fitness values + o better use the class java.lang.Object.Date to handle elapsed times? + + Last change: $Date: 2011-06-23 $ + */ + +/** + * implements the Covariance Matrix Adaptation Evolution Strategy (CMA-ES) + * for non-linear, non-convex, non-smooth, global function minimization. The CMA-Evolution Strategy + * (CMA-ES) is a reliable stochastic optimization method which should be applied, + * if derivative based methods, e.g. quasi-Newton BFGS or conjugate + * gradient, fail due to a rugged search landscape (e.g. noise, local + * optima, outlier, etc.) of the objective function. Like a + * quasi-Newton method the CMA-ES learns and applies a variable metric + * of the underlying search space. Unlike a quasi-Newton method the + * CMA-ES does neither estimate nor use gradients, making it considerably more + * reliable in terms of finding a good, or even close to optimal, solution, finally. + * + * <p>In general, on smooth objective functions the CMA-ES is roughly ten times + * slower than BFGS (counting objective function evaluations, no gradients provided). + * For up to <math>N=10</math> variables also the derivative-free simplex + * direct search method (Nelder & Mead) can be faster, but it is + * far less reliable than CMA-ES. + * + * <p>The CMA-ES is particularly well suited for non-separable + * and/or badly conditioned problems. + * To observe the advantage of CMA compared to a conventional + * evolution strategy, it will usually take about 30×<math>N</math> function + * evaluations. On difficult problems the complete + * optimization (a single run) is expected to take <em>roughly</em> between + * <math>30×N</math> and <math>300×N<sup>2</sup></math> + * function evaluations. + * + * <p>The main functionality is provided by the methods <code>double[][] {@link #samplePopulation()}</code> and + * <code>{@link #updateDistribution(double[])}</code> or <code>{@link #updateDistribution(double[][], double[])}</code>. + * Here is an example code snippet, see file + * <tt>CMAExample1.java</tt> for a similar example, and + * <tt>CMAExample2.java</tt> for a more extended example with multi-starts implemented. + * <pre> + // new a CMA-ES and set some initial values + CMAEvolutionStrategy cma = new CMAEvolutionStrategy(); + cma.readProperties(); // read options, see file CMAEvolutionStrategy.properties + cma.setDimension(10); // overwrite some loaded properties + cma.setTypicalX(0.5); // in each dimension, setInitialX can be used as well + cma.setInitialStandardDeviation(0.2); // also a mandatory setting + cma.opts.stopFitness = 1e-9; // optional setting + + // initialize cma and get fitness array to fill in later + double[] fitness = cma.init(); // new double[cma.parameters.getPopulationSize()]; + + // initial output to files + cma.writeToDefaultFilesHeaders(0); // 0 == overwrites old files + + // iteration loop + while(cma.stopConditions.getNumber() == 0) { + + // core iteration step + double[][] pop = cma.samplePopulation(); // get a new population of solutions + for(int i = 0; i < pop.length; ++i) { // for each candidate solution i + fitness[i] = fitfun.valueOf(pop[i]); // compute fitness value, where fitfun + } // is the function to be minimized + cma.updateDistribution(fitness); // use fitness array to update search distribution + + // output to files + cma.writeToDefaultFiles(); + ...in case, print output to console, eg. cma.println(), + or process best found solution, getBestSolution()... + } // while + + // evaluate mean value as it is the best estimator for the optimum + cma.setFitnessOfMeanX(fitfun.valueOf(cma.getMeanX())); // updates the best ever solution + ...retrieve best solution, termination criterion via stopConditions etc... + + return cma.getBestX(); // best evaluated search point + + * </pre> + * The output generated by the function <code>writeToDefaultFiles</code> can be + * plotted in Matlab or Scilab using <tt>plotcmaesdat.m</tt> or + * <tt>plotcmaesdat.sci</tt> respectively, see {@link #writeToDefaultFiles()}. + * +</P> +<P> The implementation follows very closely <a name=HK2004>[3]</a>. It supports small and large +population sizes, the latter by using the rank-µ-update [2], +together with weighted recombination for the covariance matrix, an +improved parameter setting for large populations [3] and an (initially) diagonal covariance matrix [5]. +The latter is particularly useful for large dimension, e.g. larger 100. +The default population size is small [1]. An +independent restart procedure with increasing population size [4] +is implemented in class <code>{@link cmaes.examples.CMAExample2}</code>.</P> + + * <P><B>Practical hint</B>: In order to solve an optimization problem in reasonable time it needs to be + * reasonably encoded. In particular the domain width of variables should be + * similar for all objective variables (decision variables), + * such that the initial standard deviation can be chosen the same + * for each variable. For example, an affine-linear transformation could be applied to + * each variable, such that its typical domain becomes the interval [0,10]. + * For positive variables a log-encoding or a square-encoding + * should be considered, to avoid the need to set a hard boundary at zero, + * see <A href="http://www.lri.fr/~hansen/cmaes_inmatlab.html#practical">here for a few more details</A>. + * </P> + +<P><B>References</B> +<UL> +<LI>[1] Hansen, N. and A. Ostermeier (2001). Completely +Derandomized Self-Adaptation in Evolution Strategies. <I>Evolutionary +Computation</I>, 9(2), pp. 159-195. +</LI> +<LI>[2] Hansen, N., S.D. Müller and +P. Koumoutsakos (2003). Reducing the Time Complexity of the +Derandomized Evolution Strategy with Covariance Matrix Adaptation +(CMA-ES). <I>Evolutionary Computation</I>, 11(1), pp. 1-18. + +<LI>[3] Hansen and Kern (2004). Evaluating the CMA Evolution +Strategy on Multimodal Test Functions. In <I> Eighth International +Conference on Parallel Problem Solving from Nature PPSN VIII, +Proceedings</I>, pp. 282-291, Berlin: Springer. +</LI> +<LI>[4] +Auger, A, and Hansen, N. (2005). A Restart CMA Evolution Strategy +With Increasing Population Size.</A> In <I>Proceedings of the IEEE +Congress on Evolutionary Computation, CEC 2005</I>, pp.1769-1776. +</LI> +<LI>[5] +Ros, R. and N. Hansen (2008). A Simple +Modification in CMA-ES Achieving Linear Time and Space Complexity. +In Rudolph et al. (eds.) <I>Parallel Problem Solving from Nature, PPSN X, +Proceedings</I>, pp. 296-305, Springer. +</LI> +</UL> +</P> + + * @see #samplePopulation() + * @see #updateDistribution(double[]) + * @author Nikolaus Hansen, 1996, 2003, 2005, 2007 + */ +public class CMAEvolutionStrategy implements java.io.Serializable { + /** + * + */ + private static final long serialVersionUID = 2918241407634253526L; + + /** + * + */ + public final String versionNumber = new String("0.99.40"); + + /** Interface to whether and which termination criteria are satisfied + */ + public class StopCondition { + int index = 0; // number of messages collected == index where to write next message + String[] messages = new String[]{""}; // Initialisation with empty string + double lastcounteval; + /** true whenever a termination criterion was met. clear() + * re-sets this value to false. + * @see #clear() + */ + public boolean isTrue() { + return test() > 0; + } + /** evaluates to NOT isTrue(). + * @see #isTrue() + */ + public boolean isFalse() { + return !isTrue(); + } + /** greater than zero whenever a termination criterion was satisfied, zero otherwise. + * clear() re-sets this value to zero. + * @return number of generated termination condition messages */ + public int getNumber() { + return test(); + } + + /** + * get description messages of satisfied termination criteria. + * The messages start with one of "Fitness:", "TolFun:", "TolFunHist:", + * "TolX:", "TolUpX:", "MaxFunEvals:", "MaxIter:", "ConditionNumber:", + * "NoEffectAxis:", "NoEffectCoordinate:". + * @return String[] s with messages of termination conditions. + * s[0].equals("") is true if no termination condition is satisfied yet + */ + public String[] getMessages() { + test(); + return messages; /* first string might be empty */ + } + /** remove all earlier termination condition messages + */ + public void clear() { + messages = new String[]{""}; + index = 0; + } + + private void appendMessage(String s) { + // could be replaced by ArrayList<String> or Vector<String> + // but also String[] can be iterated easily since version 1.5 + String [] mold = messages; + messages = new String[index + 1]; + + /* copy old messages */ + for (int i = 0; i < index; ++i) + messages[i] = mold[i]; + + messages[index++] = s + " (iter=" + countiter + ",eval=" + counteval + ")"; + } + + /** + * Tests termination criteria and evaluates to greater than zero when a + * termination criterion is satisfied. Repeated tests append the met criteria repeatedly, + * only if the evaluation count has changed. + * @return number of termination criteria satisfied + */ + int test() { + if (state < 0) + return 0; // not yet initialized + if (index > 0 && (counteval == lastcounteval + || counteval == lastcounteval+1)) // one evaluation for xmean is ignored + return index; // termination criterion already met + + lastcounteval = counteval; + + /* FUNCTION VALUE */ + if ((countiter > 1 || state >= 3) && bestever_fit <= options.stopFitness) + appendMessage("Fitness: Objective function value dropped below the target function value " + + options.stopFitness); + + /* #Fevals */ + if (counteval >= options.stopMaxFunEvals) + appendMessage("MaxFunEvals: maximum number of function evaluations " + options.stopMaxFunEvals + " reached"); + + /* #iterations */ + if (countiter >= options.stopMaxIter) + appendMessage("MaxIter: maximum number of iterations reached"); + + /* TOLFUN */ + if ((countiter > 1 || state >= 3) + && Math.max(math.max(fit.history), fit.fitness[fit.fitness.length-1].val) + - Math.min(math.min(fit.history),fit.fitness[0].val) <= options.stopTolFun) + appendMessage("TolFun: function value changes below stopTolFun=" + options.stopTolFun); + + /* TOLFUNHIST */ + if (options.stopTolFunHist >= 0 && countiter > fit.history.length) { + if (math.max(fit.history) - math.min(fit.history) <= options.stopTolFunHist) + appendMessage("TolFunHist: history of function value changes below stopTolFunHist=" + options.stopTolFunHist); + } + + /* TOLX */ + double tolx = Math.max(options.stopTolX, options.stopTolXFactor * minstartsigma); + if (sigma * maxsqrtdiagC < tolx + && sigma * math.max(math.abs(pc)) < tolx) + appendMessage("TolX or TolXFactor: standard deviation below " + tolx); + + /* TOLXUP */ + if (sigma * maxsqrtdiagC > options.stopTolUpXFactor * maxstartsigma) + appendMessage("TolUpX: standard deviation increased by more than stopTolUpXFactor=" + + options.stopTolUpXFactor + + ", larger initial standard deviation recommended"); + + /* STOPNOW */ + if (options.stopnow) + appendMessage("Manual: flag Options.stopnow set or stop now in .properties file"); + + /* Internal (numerical) stopping termination criteria */ + + /* Test each principal axis i, whether x == x + 0.1 * sigma * rgD[i] * B[i] */ + for (int iAchse = 0; iAchse < N; ++iAchse) { + int iKoo; + int l = flgdiag ? iAchse : 0; + int u = flgdiag ? iAchse+1 : N; + double fac = 0.1 * sigma * diagD[iAchse]; + for (iKoo = l; iKoo < u; ++iKoo) { + if (xmean[iKoo] != xmean[iKoo] + fac * B[iKoo][iAchse]) + break; // is OK for this iAchse + } + if (iKoo == u) // no break, therefore no change for axis iAchse + appendMessage("NoEffectAxis: Mutation " + 0.1*sigma*diagD[iAchse] + + " in a principal axis " + iAchse + " has no effect"); + } /* for iAchse */ + + /* Test whether one component of xmean is stuck */ + for (int iKoo = 0; iKoo < N; ++iKoo) { + if (xmean[iKoo] == xmean[iKoo] + 0.2*sigma*Math.sqrt(C[iKoo][iKoo])) + appendMessage("NoEffectCoordinate: Mutation of size " + + 0.2*sigma*Math.sqrt(C[iKoo][iKoo]) + + " in coordinate " + iKoo + " has no effect"); + } /* for iKoo */ + + /* Condition number */ + if (math.min(diagD) <= 0) + appendMessage("ConditionNumber: smallest eigenvalue smaller or equal zero"); + else if (math.max(diagD)/math.min(diagD) > 1e7) + appendMessage("ConditionNumber: condition number of the covariance matrix exceeds 1e14"); + return index; // call to appendMessage increments index + } + } // StopCondtion + + + void testAndCorrectNumerics() { // not much left here + + /* Flat Fitness, Test if function values are identical */ + if (getCountIter() > 1 || (getCountIter() == 1 && state >= 3)) + if (fit.fitness[0].val == fit.fitness[Math.min(sp.getLambda()-1, sp.getLambda()/2+1) - 1].val) { + warning("flat fitness landscape, consider reformulation of fitness, step-size increased"); + sigma *= Math.exp(0.2+sp.getCs()/sp.getDamps()); + } + + /* Align (renormalize) scale C (and consequently sigma) */ + /* e.g. for infinite stationary state simulations (noise + * handling needs to be introduced for that) */ + double fac = 1; + if (math.max(diagD) < 1e-6) + fac = 1./math.max(diagD); + else if (math.min(diagD) > 1e4) + fac = 1./math.min(diagD); + + if (fac != 1.) { + sigma /= fac; + for(int i = 0; i < N; ++i) { + pc[i] *= fac; + diagD[i] *= fac; + for (int j = 0; j <= i; ++j) + C[i][j] *= fac*fac; + } + } + } // Test... + + /** options that can be changed (fields can be assigned) at any time to control + * the running behavior + * */ + public CMAOptions options = new CMAOptions(); + + private CMAParameters sp = new CMAParameters(); // alias for inside use + /** strategy parameters that can be set initially + * */ + public CMAParameters parameters = sp; // for outside use also + + /** permits access to whether and which termination conditions were satisfied */ + public StopCondition stopConditions = new StopCondition(); + + int N; + long seed = System.currentTimeMillis(); + Random rand = new Random(seed); // Note: it also Serializable + + final MyMath math = new MyMath(); + double axisratio; + long counteval; + long countiter; + + long bestever_eval; // C style, TODO: better make use of class CMASolution? + double[] bestever_x; + double bestever_fit = Double.NaN; + // CMASolution bestever; // used as output variable + + double sigma = 0.0; + double[] typicalX; // eventually used to set initialX + double[] initialX; // set in the end of init() + double[] LBound, UBound; // bounds + double[] xmean; + double xmean_fit = Double.NaN; + double[] pc; + double[] ps; + double[][] C; + double maxsqrtdiagC; + double minsqrtdiagC; + double[][] B; + double[] diagD; + boolean flgdiag; // 0 == full covariance matrix + + /* init information */ + double[] startsigma; + double maxstartsigma; + double minstartsigma; + + boolean iniphase; + + /** + * state (postconditions): + * -1 not yet initialized + * 0 initialized init() + * 0.5 reSizePopulation + * 1 samplePopulation, sampleSingle, reSampleSingle + * 2.5 updateSingle + * 3 updateDistribution + */ + double state = -1; + long citerlastwritten = 0; + long countwritten = 0; + int lockDimension = 0; + int mode = 0; + final int SINGLE_MODE = 1; // not in use anymore, keep for later developements? + final int PARALLEL_MODE = 2; + + + long countCupdatesSinceEigenupdate; + + /* fitness information */ + class FitnessCollector { + + double history[]; + IntDouble[] fitness; // int holds index for respective arx + IntDouble[] raw; // sorted differently than fitness! + /** history of delta fitness / sigma^2. Here delta fitness is the minimum of + * fitness value differences with distance lambda/2 in the ranking. */ + double[] deltaFitHist = new double[5]; + int idxDeltaFitHist = 0; + } + protected FitnessCollector fit = new FitnessCollector(); + + double recentFunctionValue; + double recentMaxFunctionValue; + double recentMinFunctionValue; + int idxRecentOffspring; + + double[][] arx; + /** recent population, no idea whether this is useful to be public */ + public double[][] population; // returned not as a copy + double[] xold; + + double[] BDz; + double[] artmp; + + String propertiesFileName = new String("CMAEvolutionStrategy.properties"); + /** postpones most initialization. For initialization use setInitial... + * methods or set up a properties file, see file "CMAEvolutionStrategy.properties". */ + public CMAEvolutionStrategy() { + state = -1; + } + + /** retrieves options and strategy parameters from properties input, see file <tt>CMAEvolutionStrategy.properties</tt> + * for valid properties */ + public CMAEvolutionStrategy(Properties properties) { + setFromProperties(properties); + state = -1; + } + /** reads properties (options, strategy parameter settings) from + * file <code>propertiesFileName</code> + * */ + public CMAEvolutionStrategy(String propertiesFileName) { + this.propertiesFileName = propertiesFileName; + state = -1; + } + + /** @param dimension search space dimension, dimension of the + * objective functions preimage, number of variables + */ + public CMAEvolutionStrategy(int dimension) { + setDimension(dimension); + state = -1; + } + + /** initialization providing all mandatory input arguments at once. The following two + * is equivalent + * <PRE> + cma.init(N, X, SD); + * </PRE> and + * <PRE> + cma.setInitalX(X); // + cma.setInitialStandardDeviations(SD); + cma.init(N); + * </PRE> + * + * The call to <code>init</code> is a point of no return for parameter + * settings, and demands all mandatory input be set. <code>init</code> then forces the + * setting up of everything and calls + * <code>parameters.supplementRemainders()</code>. If <code>init</code> was not called before, it is called once in + * <code>samplePopulation()</code>. The return value is only provided for sake of convenience. + * + * @param dimension + * @param initialX double[] can be of size one, where all variables are set to the + * same value, or of size dimension + * @param initialStandardDeviations can be of size one, where all standard + * deviations are set to the same value, or of size dimension + * + * @return <code>double[] fitness</code> of length population size lambda to assign and pass + * objective function values to <code>{@link #updateDistribution(double[])}</code> + * + * @see #init() + * @see #init(int) + * @see #setInitialX(double[]) + * @see #setTypicalX(double[]) + * @see #setInitialStandardDeviations(double[]) + * @see #samplePopulation() + * @see CMAParameters#supplementRemainders(int, CMAOptions) + */ + public double[] init(int dimension, double[] initialX, double[] initialStandardDeviations) { + setInitialX(initialX); + setInitialStandardDeviations(initialStandardDeviations); + return init(dimension); + } + + private double[] getArrayOf(double x, int dim) { + double[] res = new double[dim]; + for (int i = 0; i < dim; ++i) + res[i] = x; + return res; + } + /** + * + * @param x null or x.length==1 or x.length==dim, only for the second case x is expanded + * @param dim + * @return <code>null</code> or <code>double[] x</code> with <code>x.length==dim</code> + */ + private double[] expandToDimension(double[] x, int dim) { + if (x == null) + return null; + if (x.length == dim) + return x; + if (x.length != 1) + error("x must have length one or length dimension"); + + return getArrayOf(x[0], dim); + } + + /** + * @param dimension search space dimension + * @see #init(int, double[], double[]) + * */ + public double[] init(int dimension) { + setDimension(dimension); + return init(); + } + /** + * @see #init(int, double[], double[]) + * */ + public double[] init() { + int i; + if (N <= 0) + error("dimension needs to be determined, use eg. setDimension() or setInitialX()"); + if (state >= 0) + error("init() cannot be called twice"); + if (state == 0) // less save variant + return new double[sp.getLambda()]; + if (state > 0) + error("init() cannot be called after the first population was sampled"); + + sp = parameters; /* just in case the user assigned parameters */ + if (sp.supplemented == 0) // a bit a hack + sp.supplementRemainders(N, options); + sp.locked = 1; // lambda cannot be changed anymore + + diagD = new double[N]; + for (i = 0; i < N; ++i) + diagD[i] = 1; + + /* expand Boundaries */ + LBound = expandToDimension(LBound, N); + if (LBound == null) { + LBound = new double[N]; + for (i = 0; i < N; ++i) + LBound[i] = Double.NEGATIVE_INFINITY; + } + + UBound = expandToDimension(UBound, N); + if (UBound == null) { + UBound = new double[N]; + for (i = 0; i < N; ++i) + UBound[i] = Double.POSITIVE_INFINITY; + } + + /* Initialization of sigmas */ + if (startsigma != null) { // + if (startsigma.length == 1) { + sigma = startsigma[0]; + } else if (startsigma.length == N) { + sigma = math.max(startsigma); + if (sigma <= 0) + error("initial standard deviation sigma must be positive"); + for (i = 0; i < N; ++i) { + diagD[i] = startsigma[i]/sigma; + } + } else + assert false; + } else { + // we might use boundaries here to find startsigma, but I prefer to have stddevs mandatory + error("no initial standard deviation specified, use setInitialStandardDeviations()"); + sigma = 0.5; + } + + if (sigma <= 0 || math.min(diagD) <= 0) { + error("initial standard deviations not specified or non-positive, " + + "use setInitialStandarddeviations()"); + sigma = 1; + } + /* save initial standard deviation */ + if (startsigma == null || startsigma.length == 1) { + startsigma = new double[N]; + for (i = 0; i < N; ++i) { + startsigma[i] = sigma * diagD[i]; + } + } + maxstartsigma = math.max(startsigma); + minstartsigma = math.min(startsigma); + axisratio = maxstartsigma / minstartsigma; // axis parallel distribution + + /* expand typicalX, might still be null afterwards */ + typicalX = expandToDimension(typicalX, N); + + /* Initialization of xmean */ + xmean = expandToDimension(xmean, N); + if (xmean == null) { + /* set via typicalX */ + if (typicalX != null) { + xmean = typicalX.clone(); + for (i = 0; i < N; ++i) + xmean[i] += sigma*diagD[i] * rand.nextGaussian(); + /* set via boundaries, is depriciated */ + } else if (math.max(UBound) < Double.MAX_VALUE + && math.min(LBound) > -Double.MAX_VALUE) { + error("no initial search point (solution) X or typical X specified"); + xmean = new double[N]; + for (i = 0; i < N; ++i) { /* TODO: reconsider this algorithm to set X0 */ + double offset = sigma*diagD[i]; + double range = (UBound[i] - LBound[i] - 2*sigma*diagD[i]); + if (offset > 0.4 * (UBound[i] - LBound[i])) { + offset = 0.4 * (UBound[i] - LBound[i]); + range = 0.2 * (UBound[i] - LBound[i]); + } + xmean[i] = LBound[i] + offset + rand.nextDouble() * range; + } + } else { + error("no initial search point (solution) X or typical X specified"); + xmean = new double[N]; + for (i = 0; i < N; ++i) + xmean[i] = rand.nextDouble(); + } + } + + assert xmean != null; + assert sigma > 0; + + /* interpret missing option value */ + if (options.diagonalCovarianceMatrix < 0) // necessary for hello world message + options.diagonalCovarianceMatrix = 1 * 150 * N / sp.lambda; // cave: duplication below + + /* non-settable parameters */ + pc = new double[N]; + ps = new double[N]; + B = new double[N][N]; + C = new double[N][N]; // essentially only i <= j part is used + + xold = new double[N]; + BDz = new double[N]; + bestever_x = xmean.clone(); + // bestever = new CMASolution(xmean); + artmp = new double[N]; + + + fit.deltaFitHist = new double[5]; + fit.idxDeltaFitHist = -1; + for (i = 0; i < fit.deltaFitHist.length; ++i) + fit.deltaFitHist[i] = 1.; + + // code to be duplicated in reSizeLambda + fit.fitness = new IntDouble[sp.getLambda()]; // including penalties, used yet + fit.raw = new IntDouble[sp.getLambda()]; // raw function values + fit.history = new double[10+30*N/sp.getLambda()]; + + arx = new double[sp.getLambda()][N]; + population = new double[sp.getLambda()][N]; + + for (i = 0; i < sp.getLambda(); ++i) { + fit.fitness[i] = new IntDouble(); + fit.raw[i] = new IntDouble(); + } + + // initialization + for (i = 0; i < N; ++i) { + pc[i] = 0; + ps[i] = 0; + for (int j = 0; j < N; ++j) { + B[i][j] = 0; + } + for (int j = 0; j < i; ++j) { + C[i][j] = 0; + } + B[i][i] = 1; + C[i][i] = diagD[i] * diagD[i]; + } + maxsqrtdiagC = Math.sqrt(math.max(math.diag(C))); + minsqrtdiagC = Math.sqrt(math.min(math.diag(C))); + countCupdatesSinceEigenupdate = 0; + iniphase = false; // obsolete + + /* Some consistency check */ + for (i = 0; i < N; ++i) { + if (LBound[i] > UBound[i]) + error("lower bound is greater than upper bound"); + if (typicalX != null) { + if (LBound[i] > typicalX[i]) + error("lower bound '" + LBound[i] + "'is greater than typicalX" + typicalX[i]); + if (UBound[i] < typicalX[i]) + error("upper bound '" + UBound[i] + "' is smaller than typicalX " + typicalX[i]); + } + } + String[] s = stopConditions.getMessages(); + if(!s[0].equals("")) + warning("termination condition satisfied at initialization: \n " + s[0]); + + initialX = xmean.clone(); // keep finally chosen initialX + + timings.start = System.currentTimeMillis(); + timings.starteigen = System.currentTimeMillis(); + + state = 0; + if(options.verbosity > -1) + printlnHelloWorld(); + + return new double[sp.getLambda()]; + + } // init() + + /** get default parameters in new CMAParameters instance, dimension must + * have been set before calling getDefaults + * + * @see CMAParameters#getDefaults(int) + */ + public CMAParameters getParameterDefaults() { + return sp.getDefaults(N); + } + + /** get default parameters in new CMAParameters instance + * + * @see CMAParameters#getDefaults(int) + */ + public CMAParameters getParameterDefaults(int N) { + return sp.getDefaults(N); + } + + /** reads properties from default + * input file CMAEvolutionStrategy.properties and + * sets options and strategy parameter settings + * accordingly. Options values can be changed at any time using this function. + */ + public Properties readProperties() { + return readProperties(propertiesFileName); + } + Properties properties = new Properties(); + /** reads properties from fileName and sets strategy parameters and options + * accordingly + * @param fileName of properties file + */ + public Properties readProperties(String fileName) { + this.propertiesFileName = fileName; +// if (fileName.equals("")) +// return properties; + try { + java.io.FileInputStream fis = new java.io.FileInputStream(fileName); + properties.load(fis); + fis.close(); + } + catch(java.io.IOException e) { + warning("File '" + fileName + "' not found, no options read"); + // e.printStackTrace(); + } + setFromProperties(properties); + return properties; + } + + /** reads properties from Properties class + * input and sets options and parameters accordingly + * + * @param properties java.util.Properties key-value hash table + * @see #readProperties() + */ + public void setFromProperties(Properties properties) { + String s; + + options.setOptions(properties); + + if (state >= 0) // only options can be changed afterwards + return; // defaults are already supplemented + +// if (properties.containsKey("boundaryLower") && +// properties.containsKey("boundaryUpper")) { +// setBoundaries(parseDouble(getAllToken(properties.getProperty("boundaryLower"))), +// parseDouble(getAllToken(properties.getProperty("boundaryUpper")))); + if ((s = properties.getProperty("typicalX")) != null) { + setTypicalX(options.parseDouble(options.getAllToken(s))); + } + if ((s = properties.getProperty("initialX")) != null) { + setInitialX(options.parseDouble(options.getAllToken(s))); + } + if ((s = properties.getProperty("initialStandardDeviations")) != null) { + setInitialStandardDeviations(options.parseDouble(options.getAllToken(s))); + } + if ((s = properties.getProperty("dimension")) != null) { // parseInt does not ignore trailing spaces + setDimension(Integer.parseInt(options.getFirstToken(s))); + } + if ((s = properties.getProperty("randomSeed")) != null) { + setSeed(Long.parseLong(options.getFirstToken(s))); + } + if ((s = properties.getProperty("populationSize")) != null) { + sp.setPopulationSize(Integer.parseInt(options.getFirstToken(s))); + } + if ((s = properties.getProperty("cCov")) != null) { + sp.setCcov(Double.parseDouble(options.getFirstToken(s))); + } + + } +// private void infoVerbose(String s) { +// println(" CMA-ES info: " + s); +// } + + private void warning(String s) { + println(" CMA-ES warning: " + s); + } + private void error(String s) { // somehow a relict from the C history of this code + println(" CMA-ES error: " + s); + //e.printStackTrace(); // output goes to System.err + //e.printStackTrace(System.out); // send trace to stdout + + throw new CMAException(" CMA-ES error: " + s); + // System.exit(-1); + } + + /** some simple math utilities */ + class MyMath { // implements java.io.Serializable { + int itest; + + double square(double d) { + return d*d; + } + double prod(double []ar) { + double res = 1.0; + for(int i = 0; i < ar.length; ++i) + res *= ar[i]; + return res; + } + + public double median(double ar[]) { + // need a copy of ar + double [] ar2 = new double[ar.length]; + for (int i = 0; i < ar.length; ++i) + ar2[i] = ar[i]; + Arrays.sort(ar2); + if (ar2.length % 2 == 0) + return (ar2[ar.length/2] + ar2[ar.length/2-1]) / 2.; + else + return ar2[ar.length/2]; + } + + /** @return Maximum value of 1-D double array */ + public double max(double ar[]) { + int i; + double m; + m = ar[0]; + for (i = 1; i < ar.length; ++i) { + if (m < ar[i]) + m = ar[i]; + } + return m; + } + + /** sqrt(a^2 + b^2) without under/overflow. **/ + public double hypot(double a, double b) { + double r = 0; + if (Math.abs(a) > Math.abs(b)) { + r = b/a; + r = Math.abs(a)*Math.sqrt(1+r*r); + } else if (b != 0) { + r = a/b; + r = Math.abs(b)*Math.sqrt(1+r*r); + } + return r; + } + /** @return index of minium value of 1-D double array */ + public int minidx(double ar[]) { + return minidx(ar, ar.length-1); + } + + /** @return index of minium value of 1-D double + * array between index 0 and maxidx + * @param ar double[] + * @param maxidx last index to be considered */ + public int minidx(double[] ar, int maxidx) { + int i, idx; + idx = 0; + for (i = 1; i < maxidx; ++i) { + if (ar[idx] > ar[i]) + idx = i; + } + return idx; + } + + /** @return index of minium value of 1-D double + * array between index 0 and maxidx + * @param ar double[] + * @param maxidx last index to be considered */ + protected int minidx(IntDouble[] ar, int maxidx) { + int i, idx; + idx = 0; + for (i = 1; i < maxidx; ++i) { + if (ar[idx].val > ar[i].val) + idx = i; + } + return idx; + } + + /** @return index of maximum value of 1-D double array */ + public int maxidx(double ar[]) { + int i, idx; + idx = 0; + for (i = 1; i < ar.length; ++i) { + if (ar[idx] < ar[i]) + idx = i; + } + return idx; + } + /** @return Minimum value of 1-D double array */ + public double min(double ar[]) { + int i; + double m; + m = ar[0]; + for (i = 1; i < ar.length; ++i) { + if (m > ar[i]) + m = ar[i]; + } + return m; + } + + /** @return Maximum value of 1-D Object array where the object implements Comparator + * Example: max(Double arx, arx[0]) */ + public Double max(Double ar[], Comparator<Double> c) { + int i; + Double m; + m = ar[0]; + for (i = 1; i < ar.length; ++i) { + if (c.compare(m, ar[i]) > 0) + m = ar[i]; + } + return m; + } + + /** @return Maximum value of 1-D IntDouble array */ + public IntDouble max(IntDouble ar[]) { + int i; + IntDouble m; + m = ar[0]; + for (i = 1; i < ar.length; ++i) { + if (m.compare(m, ar[i]) < 0) + m = ar[i]; + } + return m; + } + + /** @return Minimum value of 1-D IntDouble array */ + public IntDouble min(IntDouble ar[]) { + int i; + IntDouble m; + m = ar[0]; + for (i = 1; i < ar.length; ++i) { + if (m.compare(m, ar[i]) > 0) + m = ar[i]; + } + return m; + } + + /** @return Minimum value of 1-D Object array defining a Comparator */ + public Double min(Double ar[], Comparator<Double> c) { + int i; + Double m; + m = ar[0]; + for (i = 1; i < ar.length; ++i) { + if (c.compare(m, ar[i]) < 0) + m = ar[i]; + } + return m; + } + + /** + * @return Diagonal of an 2-D double array + */ + public double[] diag(double ar[][]) { + int i; + double[] diag = new double[ar.length]; + for (i = 0; i < ar.length && i < ar[i].length; ++i) + diag[i] = ar[i][i]; + return diag; + } + + /** + * @return 1-D double array of absolute values of an 1-D double array + */ + public double[] abs(double v[]) { + double res[] = new double[v.length]; + for(int i = 0; i < v.length; ++i) + res[i] = Math.abs(v[i]); + return res; + } + } // MyMath + + class Timing { + Timing(){ + birth = System.currentTimeMillis(); + start = birth; // on the save side + } + long birth; // time at construction, not really in use + long start; // time at end of init() + long starteigen; // time after flgdiag was turned off, ie when calls to eigen() start + long eigendecomposition = 0; // spent time in eigendecomposition + long writedefaultfiles = 0; // spent time in writeToDefaultFiles + } + Timing timings = new Timing(); + + /* flgforce == 1 force independent of time measurments, + * flgforce == 2 force independent of uptodate-status + */ + void eigendecomposition(int flgforce) { + /* Update B and D, calculate eigendecomposition */ + int i, j; + + if (countCupdatesSinceEigenupdate == 0 && flgforce < 2) + return; + + // 20% is usually better in terms of running *time* (only on fast to evaluate functions) + if (!flgdiag && flgforce <= 0 && + (timings.eigendecomposition > 1000 + options.maxTimeFractionForEigendecomposition + * (System.currentTimeMillis() - timings.starteigen) + || countCupdatesSinceEigenupdate < 1. / sp.getCcov() / N / 5.)) + return; + + if (flgdiag) { + for (i = 0; i < N; ++i) { + diagD[i] = Math.sqrt(C[i][i]); + } + countCupdatesSinceEigenupdate = 0; + timings.starteigen = System.currentTimeMillis(); // reset starting time + timings.eigendecomposition = 0; // not really necessary + } else { + // set B <- C + for (i = 0; i < N; ++i) + for (j = 0; j <= i; ++j) + B[i][j] = B[j][i] = C[i][j]; + + // eigendecomposition + double [] offdiag = new double[N]; + long firsttime = System.currentTimeMillis(); + tred2(N, B, diagD, offdiag); + tql2(N, diagD, offdiag, B); + timings.eigendecomposition += System.currentTimeMillis() - firsttime; + + if (options.checkEigenSystem > 0) + checkEigenSystem( N, C, diagD, B); // for debugging + + // assign diagD to eigenvalue square roots + for (i = 0; i < N; ++i) { + if (diagD[i] < 0) // numerical problem? + error("an eigenvalue has become negative"); + diagD[i] = Math.sqrt(diagD[i]); + } + countCupdatesSinceEigenupdate = 0; + } // end Update B and D + if (math.min(diagD) == 0) // error management is done elsewhere + axisratio = Double.POSITIVE_INFINITY; + else + axisratio = math.max(diagD) / math.min(diagD); + + } // eigendecomposition + + + /* ========================================================= */ + int + checkEigenSystem( int N, double C[][], double diag[], double Q[][]) + /* + exhaustive test of the output of the eigendecomposition + needs O(n^3) operations + + produces error + returns number of detected inaccuracies + */ + { + /* compute Q diag Q^T and Q Q^T to check */ + int i, j, k, res = 0; + double cc, dd; + String s; + + for (i=0; i < N; ++i) + for (j=0; j < N; ++j) { + for (cc=0.,dd=0., k=0; k < N; ++k) { + cc += diag[k] * Q[i][k] * Q[j][k]; + dd += Q[i][k] * Q[j][k]; + } + /* check here, is the normalization the right one? */ + if (Math.abs(cc - C[i>j?i:j][i>j?j:i])/Math.sqrt(C[i][i]*C[j][j]) > 1e-10 + && Math.abs(cc - C[i>j?i:j][i>j?j:i]) > 1e-9) { /* quite large */ + s = " " + i + " " + j + " " + cc + " " + C[i>j?i:j][i>j?j:i] + " " + (cc-C[i>j?i:j][i>j?j:i]); + warning("cmaes_t:Eigen(): imprecise result detected " + s); + ++res; + } + if (Math.abs(dd - (i==j?1:0)) > 1e-10) { + s = i + " " + j + " " + dd; + warning("cmaes_t:Eigen(): imprecise result detected (Q not orthog.) " + s); + ++res; + } + } + return res; + } + + + + // Symmetric Householder reduction to tridiagonal form, taken from JAMA package. + + private void tred2 (int n, double V[][], double d[], double e[]) { + + // This is derived from the Algol procedures tred2 by + // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + for (int j = 0; j < n; j++) { + d[j] = V[n-1][j]; + } + + // Householder reduction to tridiagonal form. + + for (int i = n-1; i > 0; i--) { + + // Scale to avoid under/overflow. + + double scale = 0.0; + double h = 0.0; + for (int k = 0; k < i; k++) { + scale = scale + Math.abs(d[k]); + } + if (scale == 0.0) { + e[i] = d[i-1]; + for (int j = 0; j < i; j++) { + d[j] = V[i-1][j]; + V[i][j] = 0.0; + V[j][i] = 0.0; + } + } else { + + // Generate Householder vector. + + for (int k = 0; k < i; k++) { + d[k] /= scale; + h += d[k] * d[k]; + } + double f = d[i-1]; + double g = Math.sqrt(h); + if (f > 0) { + g = -g; + } + e[i] = scale * g; + h = h - f * g; + d[i-1] = f - g; + for (int j = 0; j < i; j++) { + e[j] = 0.0; + } + + // Apply similarity transformation to remaining columns. + + for (int j = 0; j < i; j++) { + f = d[j]; + V[j][i] = f; + g = e[j] + V[j][j] * f; + for (int k = j+1; k <= i-1; k++) { + g += V[k][j] * d[k]; + e[k] += V[k][j] * f; + } + e[j] = g; + } + f = 0.0; + for (int j = 0; j < i; j++) { + e[j] /= h; + f += e[j] * d[j]; + } + double hh = f / (h + h); + for (int j = 0; j < i; j++) { + e[j] -= hh * d[j]; + } + for (int j = 0; j < i; j++) { + f = d[j]; + g = e[j]; + for (int k = j; k <= i-1; k++) { + V[k][j] -= (f * e[k] + g * d[k]); + } + d[j] = V[i-1][j]; + V[i][j] = 0.0; + } + } + d[i] = h; + } + + // Accumulate transformations. + + for (int i = 0; i < n-1; i++) { + V[n-1][i] = V[i][i]; + V[i][i] = 1.0; + double h = d[i+1]; + if (h != 0.0) { + for (int k = 0; k <= i; k++) { + d[k] = V[k][i+1] / h; + } + for (int j = 0; j <= i; j++) { + double g = 0.0; + for (int k = 0; k <= i; k++) { + g += V[k][i+1] * V[k][j]; + } + for (int k = 0; k <= i; k++) { + V[k][j] -= g * d[k]; + } + } + } + for (int k = 0; k <= i; k++) { + V[k][i+1] = 0.0; + } + } + for (int j = 0; j < n; j++) { + d[j] = V[n-1][j]; + V[n-1][j] = 0.0; + } + V[n-1][n-1] = 1.0; + e[0] = 0.0; + } + + // Symmetric tridiagonal QL algorithm, taken from JAMA package. + + private void tql2 (int n, double d[], double e[], double V[][]) { + + // This is derived from the Algol procedures tql2, by + // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + // Fortran subroutine in EISPACK. + + for (int i = 1; i < n; i++) { + e[i-1] = e[i]; + } + e[n-1] = 0.0; + + double f = 0.0; + double tst1 = 0.0; + double eps = Math.pow(2.0,-52.0); + for (int l = 0; l < n; l++) { + + // Find small subdiagonal element + + tst1 = Math.max(tst1,Math.abs(d[l]) + Math.abs(e[l])); + int m = l; + while (m < n) { + if (Math.abs(e[m]) <= eps*tst1) { + break; + } + m++; + } + + // If m == l, d[l] is an eigenvalue, + // otherwise, iterate. + + if (m > l) { + int iter = 0; + do { + iter = iter + 1; // (Could check iteration count here.) + + // Compute implicit shift + + double g = d[l]; + double p = (d[l+1] - g) / (2.0 * e[l]); + double r = math.hypot(p,1.0); + if (p < 0) { + r = -r; + } + d[l] = e[l] / (p + r); + d[l+1] = e[l] * (p + r); + double dl1 = d[l+1]; + double h = g - d[l]; + for (int i = l+2; i < n; i++) { + d[i] -= h; + } + f = f + h; + + // Implicit QL transformation. + + p = d[m]; + double c = 1.0; + double c2 = c; + double c3 = c; + double el1 = e[l+1]; + double s = 0.0; + double s2 = 0.0; + for (int i = m-1; i >= l; i--) { + c3 = c2; + c2 = c; + s2 = s; + g = c * e[i]; + h = c * p; + r = math.hypot(p,e[i]); + e[i+1] = s * r; + s = e[i] / r; + c = p / r; + p = c * d[i] - s * g; + d[i+1] = h + s * (c * g + s * d[i]); + + // Accumulate transformation. + + for (int k = 0; k < n; k++) { + h = V[k][i+1]; + V[k][i+1] = s * V[k][i] + c * h; + V[k][i] = c * V[k][i] - s * h; + } + } + p = -s * s2 * c3 * el1 * e[l] / dl1; + e[l] = s * p; + d[l] = c * p; + + // Check for convergence. + + } while (Math.abs(e[l]) > eps*tst1); + } + d[l] = d[l] + f; + e[l] = 0.0; + } + + // Sort eigenvalues and corresponding vectors. + + for (int i = 0; i < n-1; i++) { + int k = i; + double p = d[i]; + for (int j = i+1; j < n; j++) { + if (d[j] < p) { // NH find smallest k>i + k = j; + p = d[j]; + } + } + if (k != i) { + d[k] = d[i]; // swap k and i + d[i] = p; + for (int j = 0; j < n; j++) { + p = V[j][i]; + V[j][i] = V[j][k]; + V[j][k] = p; + } + } + } + } // tql2 + + /** not really in use so far, just clones and copies + * + * @param popx genotype + * @param popy phenotype, repaired + * @return popy + */ + double[][] genoPhenoTransformation(double[][] popx, double[][] popy) { + if (popy == null || popy == popx || popy.length != popx.length) + popy = new double[popx.length][]; + + for (int i = 0; i < popy.length; ++i) + popy[i] = genoPhenoTransformation(popx[i], popy[i]); + + return popy; + } + /** not really in use so far, just clones and copies + * + * @param popx genotype + * @param popy phenotype, repaired + * @return popy + */ + double[][] phenoGenoTransformation(double[][] popx, double[][] popy) { + if (popy == null || popy == popx || popy.length != popx.length) + popy = new double[popx.length][]; + + for (int i = 0; i < popy.length; ++i) + popy[i] = phenoGenoTransformation(popx[i], popy[i]); + + return popy; + } + + /** not really in use so far, just clones and copies + * + * @param x genotype + * @param y phenotype + * @return y + */ + double[] genoPhenoTransformation(double[] x, double[] y) { + if (y == null || y == x || y.length != x.length) { + y = x.clone(); + return y; // for now return an identical copy + } + for(int i = 0; i < N; ++i) + y[i] = x[i]; + return y; + } + /** not really in use so far, just clones and copies + * + * @param x genotype + * @param y phenotype + * @return y + */ + double[] phenoGenoTransformation(double[] x, double[] y) { + if (y == null || y == x || y.length != x.length) { + y = x.clone(); + return y; // for now return an identical copy + } + for(int i = 0; i < N; ++i) + y[i] = x[i]; + return y; + } + + /** + * Samples the recent search distribution lambda times + * @return double[][] population, lambda times dimension array of sampled solutions, + * where <code>lambda == parameters.getPopulationSize()</code> + * @see #resampleSingle(int) + * @see #updateDistribution(double[]) + * @see CMAParameters#getPopulationSize() + */ + public double[][] samplePopulation() { + int i, j, iNk; + double sum; + + if (state < 0) + init(); + else if (state < 3 && state > 2) + error("mixing of calls to updateSingle() and samplePopulation() is not possible"); + else + eigendecomposition(0); // latest possibility to generate B and diagD + + if (state != 1) + ++countiter; + state = 1; // can be repeatedly called without problem + idxRecentOffspring = sp.getLambda() - 1; // not really necessary at the moment + + + // ensure maximal and minimal standard deviations + if (options.lowerStandardDeviations != null && options.lowerStandardDeviations.length > 0) + for (i = 0; i < N; ++i) { + double d = options.lowerStandardDeviations[Math.min(i,options.lowerStandardDeviations.length-1)]; + if(d > sigma * minsqrtdiagC) + sigma = d / minsqrtdiagC; + } + if (options.upperStandardDeviations != null && options.upperStandardDeviations.length > 0) + for (i = 0; i < N; ++i) { + double d = options.upperStandardDeviations[Math.min(i,options.upperStandardDeviations.length-1)]; + if (d < sigma * maxsqrtdiagC) + sigma = d / maxsqrtdiagC; + } + + testAndCorrectNumerics(); + + /* sample the distribution */ + for (iNk = 0; iNk < sp.getLambda(); ++iNk) { /* + * generate scaled + * random vector (D * z) + */ + + // code duplication from resampleSingle because of possible future resampling before GenoPheno + /* generate scaled random vector (D * z) */ + if (flgdiag) + for (i = 0; i < N; ++i) + arx[iNk][i] = xmean[i] + sigma * diagD[i] * rand.nextGaussian(); + else { + for (i = 0; i < N; ++i) + artmp[i] = diagD[i] * rand.nextGaussian(); + + /* add mutation (sigma * B * (D*z)) */ + for (i = 0; i < N; ++i) { + for (j = 0, sum = 0; j < N; ++j) + sum += B[i][j] * artmp[j]; + arx[iNk][i] = xmean[i] + sigma * sum; + } + } + // redo this while isOutOfBounds(arx[iNk]) + } + + // I am desperately missing a const/readonly/visible qualifier. + return population = genoPhenoTransformation(arx, population); + + } // end samplePopulation() + + /** re-generate the <code>index</code>-th solution. After getting lambda + * solution points with samplePopulation() the i-th point, + * i=0...lambda-1, can be sampled anew by resampleSingle(i). + * + * <PRE> + * double[][] pop = cma.samplePopulation(); + * // check some stuff, i-th solution went wrong, therefore + * pop[i] = cma.resampleSingle(i); // assignment to keep the population consistent + * for (i = 0,...) + * fitness[i] = fitfun.valueof(pop[i]); + * </PRE> + * + * @see #samplePopulation() + */ + public double[] resampleSingle(int index) { + int i,j; + double sum; + if (state != 1) + error("call samplePopulation before calling resampleSingle(int index)"); + + /* sample the distribution */ + /* generate scaled random vector (D * z) */ + if (flgdiag) + for (i = 0; i < N; ++i) + arx[index][i] = xmean[i] + sigma * diagD[i] * rand.nextGaussian(); + else { + for (i = 0; i < N; ++i) + artmp[i] = diagD[i] * rand.nextGaussian(); + + /* add mutation (sigma * B * (D*z)) */ + for (i = 0; i < N; ++i) { + for (j = 0, sum = 0; j < N; ++j) + sum += B[i][j] * artmp[j]; + arx[index][i] = xmean[i] + sigma * sum; + } + } + return population[index] = genoPhenoTransformation(arx[index], population[index]); + } // resampleSingle + + /** compute Mahalanobis norm of x - mean w.r.t. the current distribution + * (using covariance matrix times squared step-size for the inner product). + * TODO: to be tested. + * @param x + * @param mean + * @return Malanobis norm of x - mean: sqrt((x-mean)' C^-1 (x-mean)) / sigma + */ + public double mahalanobisNorm(double[] x, double[] mean) { + double yi, snorm = 0; + int i, j; + // snorm = (x-mean)' Cinverse (x-mean) = (x-mean)' (BD^2B')^-1 (x-mean) + // = (x-mean)' B'^-1 D^-2 B^-1 (x-mean) + // = (x-mean)' B D^-1 D^-1 B' (x-mean) + // = (D^-1 B' (x-mean))' * (D^-1 B' (x-mean)) + /* calculate z := D^(-1) * B^(-1) * BDz into artmp, we could have stored z instead */ + for (i = 0; i < N; ++i) { + for (j = 0, yi = 0.; j < N; ++j) + yi += B[j][i] * (x[j]-mean[j]); + // yi = i-th component of B' (x-mean) + snorm += yi * yi / diagD[i] / diagD[i]; + } + return Math.sqrt(snorm) / sigma; + } + + /** update of the search distribution from a population and its + * function values, see {@link #updateDistribution(double[][], double[], 0)}. + * This might become updateDistribution(double[][], double[], popsize) + * in future. + * + * @param population double[lambda][N], lambda solutions + * @param functionValues double[lambda], respective objective values of population + * + * @see #samplePopulation() + * @see #updateDistribution(double[]) + * @see #updateDistribution(double[][], double[], int) + */ + public void updateDistribution(double[][] population, double[] functionValues) { + updateDistribution(population, functionValues, 0); + } + + /** update of the search distribution from a population and its + * function values, an alternative interface for + * {@link #updateDistribution(double[] functionValues)}. functionValues is used to establish an + * ordering of the elements in population. The first nInjected elements do not need to originate + * from #samplePopulation() or can have been modified (TODO: to be tested). + * + * @param population double[lambda][N], lambda solutions + * @param functionValues double[lambda], respective objective values of population + * @param nInjected int, first nInjected solutions of population were not sampled by + * samplePopulation() or modified afterwards + * + * @see #samplePopulation() + * @see #updateDistribution(double[]) + */ + public void updateDistribution(double[][] population, double[] functionValues, int nInjected) { + // TODO: Needs to be tested yet for nInjected > 0 + // pass first input argument + arx = phenoGenoTransformation(population, null); // TODO should still be tested + for (int i = 0; i < nInjected; ++i) { + warning("TODO: checking of injected solution has not yet been tested"); + // if (mahalanobisNorm(arx[0], xmean) > Math.sqrt(N) + 2) // testing: seems fine + // System.out.println(mahalanobisNorm(arx[i], xmean)/Math.sqrt(N)); + double upperLength = Math.sqrt(N) + 2. * N / (N+2.); // should become an interfaced parameter? + double fac = upperLength / mahalanobisNorm(arx[i], xmean); + if (fac < 1) + for (int j = 0; j < N; ++j) + arx[i][j] = xmean[j] + fac * (arx[i][j] - xmean[j]); + } + updateDistribution(functionValues); + } + + /** update of the search distribution after samplePopulation(). functionValues + * determines the selection order (ranking) for the solutions in the previously sampled + * population. This is just a different interface for updateDistribution(double[][], double[]). + * @see #samplePopulation() + * @see #updateDistribution(double[][], double[]) + */ + public void updateDistribution(double[] functionValues) { + if (state == 3) { + error("updateDistribution() was already called"); + } + if (functionValues.length != sp.getLambda()) + error("argument double[] funcionValues.length=" + functionValues.length + + "!=" + "lambda=" + sp.getLambda()); + + /* pass input argument */ + for (int i = 0; i < sp.getLambda(); ++i) { + fit.raw[i].val = functionValues[i]; + fit.raw[i].i = i; + } + + counteval += sp.getLambda(); + recentFunctionValue = math.min(fit.raw).val; + recentMaxFunctionValue = math.max(fit.raw).val; + recentMinFunctionValue = math.min(fit.raw).val; + updateDistribution(); + } + +// private IntDouble[] computePenalties() { +// int i, j, iNk; +// /* penalize repairment, eg. for boundaries */ +// // TODO: figure out whether the change of penalty is too large or fast which can disturb selection +// // this depence in particular on the length of fit.medianDeltaFit +// if (true || countiter < fit.deltaFitHist.length || countiter % 1*(N+2) == 0) { +// // minimum of differences with distance lambda/2, better the 25%tile? +// // assumes sorted array!! +// int ii = (sp.getLambda()) / 2; +// double medianDeltaFit = Math.abs(fit.funValues[ii].d - fit.funValues[0].d); +// for (i = 1; i + ii < sp.getLambda(); ++i) +// // minimum because of outliers +// medianDeltaFit = Math.min(medianDeltaFit, Math.abs(fit.funValues[ii+i].d - fit.funValues[i].d)); +// medianDeltaFit /= sigma * sigma; // should be somehow constant, because dfit depends on sigma (verified on sphere) +// if (medianDeltaFit > 0) { +//// System.out.println("set" + medianDeltaFit + " " + math.median(fit.medianDeltaFit)); +// if (fit.idxDeltaFitHist == -1) // first time: write all fields +// for (i = 0; i < fit.deltaFitHist.length; ++i) +// fit.deltaFitHist[i] = medianDeltaFit; +// if (++fit.idxDeltaFitHist == fit.deltaFitHist.length) +// fit.idxDeltaFitHist = 0; +// // save last five values in fit.medianDeltaFit +// fit.deltaFitHist[fit.idxDeltaFitHist] = medianDeltaFit; +// } +// } +// /* calculate fitness by adding function value and repair penalty */ +// double penfactor = 1. * 5. * math.median(fit.deltaFitHist); +// for (iNk = 0; iNk < sp.getLambda(); ++iNk) { +// double sqrnorm = 0; +// double prod = Math.pow(math.prod(diagD), 1.0/(double)N); +// /* calculate C^-1-norm of Delta x: norm(D^(-1) * B^(-1) * (Delta x))^2 */ +// for (i = 0; i < N; ++i) { +// double sum = 0.0; +// for (j = 0, sum = 0.; j < N; ++j) +// sum += B[j][i] * ((arxrepaired[fit.funValues[iNk].i][j] - arx[fit.funValues[iNk].i][j])); +// sqrnorm += math.square(sum / (Math.pow(diagD[i], 0.9) * Math.pow(prod, 0.10))); // regularization to I +// } +// // sqrnorm/N equals approximately 1/sigma^2 +// fit.fitness[iNk].d = fit.funValues[iNk].d + penfactor * sqrnorm / (N+2); // / (sigma * sigma); +// fit.fitness[iNk].i = fit.funValues[iNk].i; +// // System.out.println(math.median(fit.medianDeltaFit) + " " + sqrnorm / (N+2)); // / (sigma * sigma)); +// } +//// if (countiter % 10 == 1) +//// System.out.println(math.median(fit.medianDeltaFit) + " " + sqrnorm); +// return fit.fitness; +// +// } + + private void updateDistribution() { + + int i, j, k, iNk, hsig; + double sum; + double psxps; + + if (state == 3) { + error("updateDistribution() was already called"); + } + + /* sort function values */ + Arrays.sort(fit.raw, fit.raw[0]); + + for (iNk = 0; iNk < sp.getLambda(); ++iNk) { + fit.fitness[iNk].val = fit.raw[iNk].val; // superfluous at time + fit.fitness[iNk].i = fit.raw[iNk].i; + } + + /* update fitness history */ + for (i = fit.history.length - 1; i > 0; --i) + fit.history[i] = fit.history[i - 1]; + fit.history[0] = fit.raw[0].val; + + /* save/update bestever-value */ + updateBestEver(arx[fit.raw[0].i], fit.raw[0].val, + counteval - sp.getLambda() + fit.raw[0].i + 1); + + /* re-calculate diagonal flag */ + flgdiag = (options.diagonalCovarianceMatrix == 1 || options.diagonalCovarianceMatrix >= countiter); + if (options.diagonalCovarianceMatrix == -1) // options might have been re-read + flgdiag = (countiter <= 1 * 150 * N / sp.lambda); // CAVE: duplication of "default" + + /* calculate xmean and BDz~N(0,C) */ + for (i = 0; i < N; ++i) { + xold[i] = xmean[i]; + xmean[i] = 0.; + for (iNk = 0; iNk < sp.getMu(); ++iNk) + xmean[i] += sp.getWeights()[iNk] * arx[fit.fitness[iNk].i][i]; + BDz[i] = Math.sqrt(sp.getMueff()) * (xmean[i] - xold[i]) / sigma; + } + + /* cumulation for sigma (ps) using B*z */ + if (flgdiag) { + /* given B=I we have B*z = z = D^-1 BDz */ + for (i = 0; i < N; ++i) { + ps[i] = (1. - sp.getCs()) * ps[i] + + Math.sqrt(sp.getCs() * (2. - sp.getCs())) + * BDz[i] / diagD[i]; + } + } else { + /* calculate z := D^(-1) * B^(-1) * BDz into artmp, we could have stored z instead */ + for (i = 0; i < N; ++i) { + for (j = 0, sum = 0.; j < N; ++j) + sum += B[j][i] * BDz[j]; + artmp[i] = sum / diagD[i]; + } + /* cumulation for sigma (ps) using B*z */ + for (i = 0; i < N; ++i) { + for (j = 0, sum = 0.; j < N; ++j) + sum += B[i][j] * artmp[j]; + ps[i] = (1. - sp.getCs()) * ps[i] + + Math.sqrt(sp.getCs() * (2. - sp.getCs())) * sum; + } + } + + /* calculate norm(ps)^2 */ + psxps = 0; + for (i = 0; i < N; ++i) + psxps += ps[i] * ps[i]; + + /* cumulation for covariance matrix (pc) using B*D*z~N(0,C) */ + hsig = 0; + if (Math.sqrt(psxps) + / Math.sqrt(1. - Math.pow(1. - sp.getCs(), 2. * countiter)) + / sp.chiN < 1.4 + 2. / (N + 1.)) { + hsig = 1; + } + for (i = 0; i < N; ++i) { + pc[i] = (1. - sp.getCc()) * pc[i] + hsig + * Math.sqrt(sp.getCc() * (2. - sp.getCc())) * BDz[i]; + } + + /* stop initial phase, not in use anymore as hsig does the job */ + if (iniphase + && countiter > Math.min(1 / sp.getCs(), 1 + N / sp.getMucov())) + if (psxps / sp.getDamps() + / (1. - Math.pow((1. - sp.getCs()), countiter)) < N * 1.05) + iniphase = false; + + /* this, it is harmful in a dynamic environment + * remove momentum in ps, if ps is large and fitness is getting worse */ +// if (1 < 3 && psxps / N > 1.5 + 10 * Math.sqrt(2. / N) +// && fit.history[0] > fit.history[1] && fit.history[0] > fit.history[2]) { +// double tfac; +// +// infoVerbose(countiter + ": remove momentum " + psxps / N + " " +// + ps[0] + " " + sigma); +// +// tfac = Math.sqrt((1 + Math.max(0, Math.log(psxps / N))) * N / psxps); +// for (i = 0; i < N; ++i) +// ps[i] *= tfac; +// psxps *= tfac * tfac; +// } + + /* update of C */ + if (sp.getCcov() > 0 && iniphase == false) { + + ++countCupdatesSinceEigenupdate; + + /* update covariance matrix */ + for (i = 0; i < N; ++i) + for (j = (flgdiag ? i : 0); + j <= i; ++j) { + C[i][j] = (1 - sp.getCcov(flgdiag)) + * C[i][j] + + sp.getCcov() + * (1. / sp.getMucov()) + * (pc[i] * pc[j] + (1 - hsig) * sp.getCc() + * (2. - sp.getCc()) * C[i][j]); + for (k = 0; k < sp.getMu(); ++k) { /* + * additional rank mu + * update + */ + C[i][j] += sp.getCcov() * (1 - 1. / sp.getMucov()) + * sp.getWeights()[k] + * (arx[fit.fitness[k].i][i] - xold[i]) + * (arx[fit.fitness[k].i][j] - xold[j]) / sigma + / sigma; + } + } + maxsqrtdiagC = Math.sqrt(math.max(math.diag(C))); + minsqrtdiagC = Math.sqrt(math.min(math.diag(C))); + } // update of C + + /* update of sigma */ + sigma *= Math.exp(((Math.sqrt(psxps) / sp.chiN) - 1) * sp.getCs() + / sp.getDamps()); + + state = 3; + + } // updateDistribution() + + /** assigns lhs to a different instance with the same values, + * sort of smart clone, but it may be that clone is as smart already + * + * @param rhs + * @param lhs + * @return + */ + double[] assignNew(double[] rhs, double[] lhs) { + assert rhs != null; // will produce an error anyway + if(lhs != null && lhs != rhs && lhs.length == rhs.length) + for(int i = 0; i < lhs.length; ++i) + lhs[i] = rhs[i]; + else + lhs = rhs.clone(); + return lhs; + } + void updateBestEver(double[] x, double fitness, long eval) { + if (fitness < bestever_fit || Double.isNaN(bestever_fit)) { // countiter == 1 not needed anymore + bestever_fit = fitness; + bestever_eval = eval; + bestever_x = assignNew(x, bestever_x); // save (hopefully) efficient assignment + } + } + + /** ratio between length of longest and shortest axis + * of the distribution ellipsoid, which is the square root + * of the largest divided by the smallest eigenvalue of the covariance matrix + */ + public double getAxisRatio() { + return axisratio; + } + + /** get best evaluated solution found so far. + * Remark that the distribution mean was not evaluated + * but is expected to have an even better function value. + * <p>Example: getBestSolution + * @return best solution (search point) found so far + * @see #getBestRecentSolution() + * @see #getBestX() + * @see #getMeanX() */ + public CMASolution getBestSolution() { + return new CMASolution(bestever_x, bestever_fit, bestever_eval); + } + + /** eventually replaces the best-ever solution + * + * @param fitness function value computed for the solution {@link #getMeanX()} + * @return best-ever solution + */ + public CMASolution setFitnessOfMeanX(double fitness) { + xmean_fit = fitness; + ++counteval; + updateBestEver(xmean, fitness, counteval); + return new CMASolution(bestever_x, bestever_fit, bestever_eval); + } + + /** get best evaluated search point found so far. + * Remark that the distribution mean was not evaluated + * but is expected to have an even better function value. + * @return best search point found so far as double[] + * @see #getMeanX() */ + public double[] getBestX() { + if (state < 0) + return null; + return bestever_x.clone(); + } + + /** objective function value of best solution found so far. + * @return objective function value of best solution found so far + * @see #getBestSolution() + */ + public double getBestFunctionValue() { + if (state < 0) + return Double.NaN; + return bestever_fit; + } + /* * evaluation count when the best solution was found + * + */ + public long getBestEvaluationNumber() { + return bestever_eval; + } + + /** Get best evaluated solution of the last (recent) iteration. + * This solution is supposed to be more robust than the + * best ever solution in particular in possible case of + * mis-attributed good fitness values. + * Remark that the distribution mean was not evaluated + * but is expected to have an better function value. + * @return best solution (search point) in recent iteration + * @see #getBestSolution() + * @see #getBestRecentX() + * @see #getMeanX() */ + public ISolutionPoint getBestRecentSolution() { + return new CMASolution(genoPhenoTransformation(arx[fit.raw[0].i], null), + fit.raw[0].val, + counteval - sp.getLambda() + fit.raw[0].i + 1); + } + + /** best search point of the recent iteration. + * @return Returns the recentFunctionValue. + * @see #getBestRecentFunctionValue() + */ + public double[] getBestRecentX() { + return genoPhenoTransformation(arx[fit.raw[0].i], null); + } + + /** objective function value of the, + * best solution in the + * recent iteration (population) + * @return Returns the recentFunctionValue. + * @see #getBestEvaluationNumber() + * @see #getBestFunctionValue() + */ + public double getBestRecentFunctionValue() { + return recentMinFunctionValue; + } + + /** objective function value of the, + * worst solution of the recent iteration. + * @return Returns the recentMaxFunctionValue. + */ + public double getWorstRecentFunctionValue() { + return recentMaxFunctionValue; + } + + /** Get mean of the current search distribution. The mean should + * be regarded as the best estimator for the global + * optimimum at the given iteration. In particular for noisy + * problems the distribution mean is the solution of choice + * preferable to the best or recent best. The return value is + * <em>not</em> a copy. Therefore it should not be change it, without + * deep knowledge of the code (the effect of a mean change depends on + * the chosen transscription/implementation of the algorithm). + * @return mean value of the current search distribution + * @see #getBestX() + * @see #getBestRecentX() + */ + public double[] getMeanX() { + return xmean.clone(); + } + + public int getDimension() { + return N; + } + + /** + * number of objective function evaluations counted so far + */ + public long getCountEval() { + return counteval; + } + + /** + * number of iterations conducted so far + */ + public long getCountIter() { + return countiter; + } + + /** the final setting of initial <code>x</code> can + * be retrieved only after <code>init()</code> was called + * + * @return <code>double[] initialX</code> start point chosen for + * distribution mean value <code>xmean</code> + */ + public double[] getInitialX() { + if (state < 0) + error("initiaX not yet available, init() must be called first"); + return initialX.clone(); + } + + + + /** get used random number generator instance */ + public Random getRand() { + return rand; + } + + /** get properties previously read from a property file. + * + * @return java.util.Properties key-value hash table + * @see #readProperties() + */ + public Properties getProperties() { + return properties; + } + + /**@see #setSeed(long) */ + public long getSeed() { + return seed; + } +// /** Set lower and upper boundary in all variables +// * +// * @param xlow +// * @param xup +// */ +// public void setBoundaries(double xlow, double xup) { +// int len = 1; +// if (N > 0) +// len = N; +// LBound = new double[len]; +// UBound = new double[len]; +// for (int i= 0; i < len; ++i) { +// LBound[i] = xlow; +// UBound[i] = xup; +// } +// } +// /** sets lower and upper boundaries in all variables. +// * +// * @param xlow lower boundary double[], can be 1-D or of length of the number of variables (dimension). +// * @param xup see xlow +// */ +// public void setBoundaries(double[] xlow, double[] xup) { +// if( xlow == null || xup == null) +// error("boundaries cannot be null"); +// if (xlow.length == 1 && xup.length == 1) { +// setBoundaries(xlow[0], xup[0]); +// return; +// } +// if ((N > 0 && (N != xlow.length || N != xup.length)) +// || (xlow.length != xup.length)) +// error("dimensions of boundaries do not match"); +// this.LBound = xlow; +// this.UBound = xup; +// N = xlow.length; // changes N only if N was 0 +// } + + /** + * number of objective function evaluations counted so far + */ + public long setCountEval(long c) { + return counteval = c; + } + +/** search space dimensions must be set before the optimization is started. */ + public void setDimension(int n) { + if ((lockDimension > 0 || state >= 0) && N != n) + error("dimension cannot be changed anymore or contradicts to initialX"); + N = n; + } + + /** sets typicalX value, the same value in each coordinate + * @see #setTypicalX(double[]) + */ + public void setTypicalX(double x) { + if (state >= 0) + error("typical x cannot be set anymore"); + typicalX = new double[]{x}; // allows "late binding" of dimension + } + + /** sets typicalX value, which will be overwritten by initialX setting from properties + * or {@link #setInitialX(double[])} function call. + * Otherwise the initialX is sampled normally distributed from typicalX with initialStandardDeviations + * + * @see #setTypicalX(double) + * @see #setInitialX(double[]) + * @see #setInitialStandardDeviations(double[]) + */ + public void setTypicalX(double[] x) { + if (state >= 0) + error("typical x cannot be set anymore"); + if (x.length == 1) { // to make properties work + setTypicalX(x[0]); + return; + } + if (N < 1) + setDimension(x.length); + if (N != x.length) + error("dimensions N=" + N + " and input x.length=" + x.length + "do not agree"); + typicalX = new double[N]; + for (int i = 0; i < N; ++i) + typicalX[i] = x[i]; + lockDimension = 1; + } + + public void setInitialStandardDeviation(double startsigma) { + if (state >= 0) + error("standard deviations cannot be set anymore"); + this.startsigma = new double[]{startsigma}; + } + + public void setInitialStandardDeviations(double[] startsigma) { + // assert startsigma != null; // assert should not be used for public arg check + if (state >= 0) + error("standard deviations cannot be set anymore"); + if (startsigma.length == 1) { // to make properties work + setInitialStandardDeviation(startsigma[0]); + return; + } + if (N > 0 && N != startsigma.length) + error("dimensions N=" + N + " and input startsigma.length=" + + startsigma.length + "do not agree"); + if (N == 0) + setDimension(startsigma.length); + assert N == startsigma.length; + this.startsigma = startsigma.clone(); + lockDimension = 1; + } + + /** sets <code>initialX</code> to the same value in each coordinate + * + * @param x value + * @see #setInitialX(double[]) + */ + public void setInitialX(double x) { + if (state >= 0) + error("initial x cannot be set anymore"); + xmean = new double[]{x}; // allows "late binding" of dimension N + } + + /** set initial seach point <code>xmean</code> coordinate-wise uniform + * between <code>l</code> and <code>u</code>, + * dimension needs to have been set before + * + * @param l double lower value + * @param u double upper value + * @see #setInitialX(double[]) + * @see #setInitialX(double[], double[]) + * */ + public void setInitialX(double l, double u) { + if (state >= 0) + error("initial x cannot be set anymore"); + if (N < 1) + error("dimension must have been specified before"); + xmean = new double[N]; + for (int i = 0; i < xmean.length; ++i) + xmean[i] = l + (u-l) * rand.nextDouble(); + lockDimension = 1; + } + + /** set initial seach point <code>x</code> coordinate-wise uniform + * between <code>l</code> and <code>u</code>, + * dimension needs to have been set before + * @param l double lower value + * @param u double upper value */ + public void setInitialX(double[] l, double[] u) { + if (state >= 0) + error("initial x cannot be set anymore"); + if (l.length != u.length) + error("length of lower and upper values disagree"); + setDimension(l.length); + xmean = new double[N]; + for (int i = 0; i < xmean.length; ++i) + xmean[i] = l[i] + (u[i]-l[i]) * rand.nextDouble(); + lockDimension = 1; + } + + /** set initial search point to input value <code>x</code>. <code>x.length==1</code> is possible, otherwise + * the search space dimension is set to <code>x.length</code> irrevocably + * + * @param x double[] initial point + * @see #setInitialX(double) + * @see #setInitialX(double, double) + */ + public void setInitialX(double[] x) { + if (state >= 0) + error("initial x cannot be set anymore"); + if (x.length == 1) { // to make properties work + setInitialX(x[0]); + return; + } + if (N > 0 && N != x.length) + error("dimensions do not match"); + if (N == 0) + setDimension(x.length); + assert N == x.length; + xmean = new double[N]; + for (int i = 0; i < N; ++i) + xmean[i] = x[i]; + lockDimension = 1; // because xmean is set up + } + + public void setRand(Random rand) { + this.rand = rand; + } + + /** Setter for the seed for the random number generator + * java.util.Random(seed). Changing the seed will only take + * effect before {@link #init()} was called. + *@param seed a long value to initialize java.util.Random(seed) + */ + public void setSeed(long seed) { + if (state >= 0) + warning("setting seed has no effect at this point"); + else { + if (seed <= 0) + seed = System.currentTimeMillis(); + this.seed = seed; + rand.setSeed(seed); + } + } + + /** printing output in a viewable formatting style. The printing + * <pre> + * Iteration,#Fevals: rb Function Value Delta( best ,worst) |idx: Max SD idx: Min SD | minsigD sigma Axisratio | time, in eig + * 164( 8), 1638: 5.5061568003892640e-08 (-4e-08,3e-08) | 0: 3.3e-05 8: 1.5e-05 | 1.4e-05 5.6e-05 2.34 | 0.1 0.0 + *</pre> + * shows the value of getPrintAnnotation() in the first line and in the second line + * <li>164 iteration number + * <li>( 8) recently sampled search point in this iteration, + * <li>1638: number of function evaluations + * <li>5.5061568003892640e-08 objective function value F of the best point + * in the recent generation + * <li>(-4e-08, difference between the best ever evaluated function value to F, + * <li>3e-08) | difference between the worst function value of the recent generation to F + * <li>0: index of coordinate with largest standard deviation + * <li>3.3e-05 respective standard deviation + * <li>8: index of coordinate with smallest standard deviation + * <li>1.5e-05 | respective standard deviation + * <li>index of coordinate with smallest standard deviation: respective standard deviation + * <li>| 1.4e-05 standard deviation in smallest principal axis direction + * <li> 5.6e-05 sigma + * <li> 2.34 axisratio, ie. quotient between the standard deviations in largest an + * smallest principal axis directions, ie. square root of the quotient between largest + * and smallest eigenvalue of covariance matrix C + * <li> 0.1 time, overall elapsed time in seconds + * <li> 0.0 in eig, overall time spent within eigendecompostion + * @see #getPrintAnnotation() + * */ + public String getPrintLine() { + /* String.format(Locale.US, " %1$4d(%2$2d): %3$5d ", + new Object[]{ + new Long(countiter), + new Integer(idxRecentOffspring), + new Long(counteval) + }) + + String.format(Locale.US, "%1$.16e (%2$+.0e %3$.0e)", + new Object[]{ + new Double(recentFunctionValue), + new Double(getBestFunctionValue() - recentFunctionValue), + new Double(recentMaxFunctionValue - recentFunctionValue) + }) + + String.format(Locale.US, "%1$7.2f ", + new Object[]{ + new Double(axisratio) + }) + + String.format(Locale.US, "%1$2d:%2$8.1e %3$2d:%4$8.1e", + new Object[]{ + new Integer(math.minidx(math.diag(C))), + new Double(sigma * + Math.sqrt(math.min(math.diag(C)))), + new Integer(math.maxidx(math.diag(C))), + new Double(sigma * + Math.sqrt(math.max(math.diag(C)))) + }) + */ + String s; + if (state < 0) + s = new String( + new PrintfFormat(Locale.US, " %4d").sprintf(countiter) + + new PrintfFormat(Locale.US, "(%2d), ").sprintf(0) + + new PrintfFormat(Locale.US, "%6.0d: ").sprintf(counteval)); + else + s = new String( + new PrintfFormat(Locale.US, " %4d").sprintf(countiter) + + new PrintfFormat(Locale.US, "(%2d), ").sprintf(idxRecentOffspring+1) + + new PrintfFormat(Locale.US, "%6.0d: ").sprintf(counteval) + + new PrintfFormat(Locale.US, "%.16e ").sprintf(recentFunctionValue) + + new PrintfFormat(Locale.US, "(%+.0e,").sprintf(getBestFunctionValue() - recentFunctionValue) + + new PrintfFormat(Locale.US, "%.0e) | ").sprintf(recentMaxFunctionValue - recentFunctionValue) + + new PrintfFormat(Locale.US, "%2d:").sprintf(math.maxidx(math.diag(C))) + + new PrintfFormat(Locale.US, "%8.1e ").sprintf(sigma * maxsqrtdiagC) + + new PrintfFormat(Locale.US, "%2d:").sprintf(math.minidx(math.diag(C))) + + new PrintfFormat(Locale.US, "%8.1e ").sprintf(sigma * minsqrtdiagC) + + new PrintfFormat(Locale.US, "| %6.1e ").sprintf(sigma*math.min(diagD)) + + new PrintfFormat(Locale.US, "%6.1e ").sprintf(sigma) + + new PrintfFormat(Locale.US, "%6.2f").sprintf(axisratio) + + new PrintfFormat(Locale.US, " | %4.1f ").sprintf((System.currentTimeMillis()-timings.start) / 1000.) + + new PrintfFormat(Locale.US, "%4.1f ").sprintf(timings.eigendecomposition / 1000.) + ); + + return s; + + /* + return new String( + new Long(countiter) + + " " + new Integer(idxRecentOffspring) + + " " + new Long(counteval) + + " " + new Double(recentFunctionValue) + // + " " + new Double(FunctionValue() - recentFunctionValue) + // + " " + new Double(recentMaxFunctionValue - recentFunctionValue) + + " " + new Double(axisratio) + + " " + new Integer(math.minidx(math.diag(C))) + + " " + new Double(sigma * + Math.sqrt(math.min(math.diag(C)))) + + " " + new Integer(math.maxidx(math.diag(C))) + + " " + new Double(sigma * + Math.sqrt(math.max(math.diag(C)))) + ); + */ + /* formatting template + String.format(Locale.US, "%1$6.2e %2$+.0e", + new Object[]{ + new Double(), + new Double() + }) + + */ + // out.print(math.min(diagD)); + // out.print(" "); + // new DecimalFormat("0.00E0").format((3.34)) + " " + + // (cma.fit.fitness[(cma.parameters.getLambda()/2)].d + // - cma.fit.fitness[0].d) + "," + + // cma.fit.fitness[cma.parameters.getLambda()-1].d + ") | " + + + } + + /** returns an annotation string for the printings of method println(). */ + public String getPrintAnnotation() { + String s = new String( + "Iteration,#Fevals: rb Function Value Delta( best ,worst) |idx: Max SD idx: Min SD | minsigD sigma Axisratio | time, in eig"); + // 491( 3), 3924: 1.1245467061992267e+00 (-2e-01,4e-01) 9: 7.8e-05 2: 5.0e-02 | 5.9e-03 1.3e-02 660.41 + + return s; + } + + /** returns an informative initial message of the CMA-ES optimizer */ + public String helloWorld() { + String s = new String( + "(" + sp.getMu() + "," + sp.getLambda() + + ")-CMA-ES(mu_eff=" + Math.round(10.*sp.getMueff())/10. + "), Ver=\"" + + versionNumber + + "\", dimension=" + N + + ", " + options.diagonalCovarianceMatrix + " diagonal iter." + + ", randomSeed=" + seed + + " (" + new Date().toString() + ")"); + return s; + + } + /** calls System.out.println(s) and writes s to the file outcmaesdisp.dat + * by default, if writeDisplayToFile option is > 0 + * @see #getPrintLine() + */ + public void println(String s) { + System.out.println(s); + if (options.writeDisplayToFile > 0) + writeToFile(options.outputFileNamesPrefix + "disp" + ".dat", s, 1); + } + + /** calls println(getPrintLine()) + * @see #getPrintLine() + */ + public void println() { + println(getPrintLine()); + } + + /** @see #getPrintAnnotation() */ + public void printlnAnnotation() { + println(getPrintAnnotation()); + } + + /** calls println(helloWorld()) + * @see #helloWorld() + * @see #println(String) + */ + public void printlnHelloWorld() { + println(helloWorld()); + } + + public String getDataRowFitness() { + String s = new String(); + s = countiter + " " + counteval + " " + sigma + " " + axisratio + " " + + bestever_fit + " "; + if (mode == SINGLE_MODE) + s += recentFunctionValue + " "; + else { + s += fit.raw[0].val + " "; + s += fit.raw[sp.getLambda()/2].val + " "; + s += fit.raw[sp.getLambda()-1].val + " "; + s += math.min(diagD) + " " + + (math.maxidx(math.diag(C))+1) + " " + sigma*maxsqrtdiagC + " " + + (math.minidx(math.diag(C))+1) + " " + sigma*minsqrtdiagC; + //for (int i = 0; i < sp.getLambda(); ++i) { + // s += fit.funValues[i].d + " "; + //} + } + return s; + } + + public String getDataRowXRecentBest() { + int idx = 0; + if (mode == SINGLE_MODE) + idx = idxRecentOffspring; + String s = new String(); + s = countiter + " " + counteval + " " + sigma + " 0 " + + (state == 1 ? Double.NaN : fit.raw[idx].val) + " "; + for (int i = 0; i < N; ++i) { + s += arx[fit.raw[idx].i][i] + " "; + } + return s; + } + + public String getDataRowXMean() { + String s = new String(); + s = countiter + " " + counteval + " " + sigma + " 0 0 "; + for (int i = 0; i < N; ++i) { + s += xmean[i] + " "; + } + return s; + } + /** 6-th to last column are sorted axis lengths axlen */ + public String getDataRowAxlen() { + String s = new String(); + s = countiter + " " + counteval + " " + sigma + " " + axisratio + " " + + maxsqrtdiagC/minsqrtdiagC + " "; + double[] tmp = (double[]) diagD.clone(); + java.util.Arrays.sort(tmp); + for (int i = 0; i < N; ++i) { + s += tmp[i] + " "; + } + return s; + } + public String getDataRowStddev() { + String s = new String(); + s = countiter + " " + counteval + " " + sigma + " " + + (1+math.maxidx(math.diag(C))) + " " + (1+math.minidx(math.diag(C))) + " "; + for (int i = 0; i < N; ++i) { + s += sigma * Math.sqrt(C[i][i]) + " "; + } + return s; + } + /** correlations and covariances of the search distribution. The + * first, '%#'-commented row contains itertation number, + * evaluation number, and sigma. In the remaining rows the upper + * triangular part contains variances and covariances + * sigma*sigma*c_ij. The lower part contains correlations c_ij / + * sqrt(c_ii * c_jj). */ + public String getDataC() { + int i, j; + String s = new String(); + s = "%# " + countiter + " " + counteval + " " + sigma + "\n"; + for (i = 0; i < N; ++i) { + for (j = 0; j < i; ++j) // ouput correlation in the lower half + s += C[i][j] / Math.sqrt(C[i][i] * C[j][j]) + " "; + for (j = i; j < N; ++j) + s += sigma * sigma * C[i][j] + " "; + s += "\n"; + } + return s; + } + + private String[] fileswritten = new String[]{""}; // also (re-)initialized in init() + /** writes a string to a file, overwrites first, appends afterwards. + * <p>Example: cma.writeToFile("cmaescorr.dat", cma.writeC()); + * @param filename is a String giving the name of the file to be written + * @param data is a String of text/data to be written + * @param flgAppend for flgAppend>0 old data are not overwritten + */ + public void writeToFile(String filename, String data, int flgAppend) { + boolean appendflag = flgAppend > 0; + for (int i = 0; !appendflag && i < fileswritten.length; ++i) + if(filename.equals(fileswritten[i])) { + appendflag = true; + } + java.io.PrintWriter out = null; + try { + out = new java.io.PrintWriter(new java.io.FileWriter(filename, appendflag)); + out.println(data); + out.flush(); // no idea whether this makes sense + out.close(); + } catch (java.io.FileNotFoundException e) { + warning("Could not find file '" + filename + "'(FileNotFoundException)"); + } catch (java.io.IOException e) { + warning("Could not open/write to file " + filename); + //e.printStackTrace(); // output goes to System.err + //e.printStackTrace(System.out); // send trace to stdout + } finally { + if (out != null) + out.close(); + } + // if first time written + // append filename to fileswritten + if (appendflag == false) { + String s[] = fileswritten; + fileswritten = new String[fileswritten.length+1]; + for (int i = 0; i < s.length; ++i) + fileswritten[i] = s[i]; + fileswritten[fileswritten.length-1] = new String(filename); + } + } + /** writes data output to default files. Uses opts.outputFileNamesPrefix to create filenames. + * Columns 1-2 are iteration number and function evaluation count, + * columns 6- are the data according to the filename. Maximum time spent + * for writing can be controlled in the properties file. + * + * <p>The output is written to files that can be printed in Matlab or Scilab (a free + * and easy to install Matlab "clone").</p> + * <p> + * Matlab: + * <pre> + cd 'directory_where_outfiles_and_plotcmaesdat.m_file_are' + plotcmaesdat; + * </pre> + * Scilab: + * <pre> + cd 'directory_where_outfiles_and_plotcmaesdat.sci_file_are' + getf('plotcmaesdat.sci'); + plotcmaesdat; + * </pre> + * </p> + * @see #writeToDefaultFiles(String fileNamePrefix) + * @see #writeToDefaultFiles(int) + * */ + public void writeToDefaultFiles() { + writeToDefaultFiles(options.outputFileNamesPrefix); + } + /** writes data output to default files. Maximum time spent + * for writing can be controlled in the properties file. For negative values + * no writing takes place, overruling the <code>flgForce</code> input parameter below. + * + * @param flgForce 0==write depending on time spent with writing, + * 1==write if the iteration count has changed, + * 2==write always, overruled by negative values of maxTimeFractionForWriteToDefaultFiles property + * + * @see #writeToDefaultFiles() */ + public void writeToDefaultFiles(int flgForce) { + if (flgForce > 0 && countiter != citerlastwritten) + citerlastwritten = -1; // force writing if something new is there + if (flgForce >= 2) + citerlastwritten = -1; // force writing + writeToDefaultFiles(options.outputFileNamesPrefix); + } + /** + * writes data to files <tt>fileNamePrefix</tt>fit.dat, ...xmean.dat + * ...xbest.dat, ...std.dat, ...axlen.dat. + * @see #writeToDefaultFiles() + * @param fileNamePrefix prefix String for filenames created to write data */ + public void writeToDefaultFiles(String fileNamePrefix) { + + if (options.maxTimeFractionForWriteToDefaultFiles < 0) // overwrites force flag + return; + if (citerlastwritten >= 0) { // negative value forces writing + if (state < 1) + return; + if (countiter == citerlastwritten) + return; + if (options.maxTimeFractionForWriteToDefaultFiles <= 0) + return; + if (countiter > 4 && stopConditions.index == 0 // has no effect if stopCondition.test() was not called + // iteration gap is less than two times of the average gap, to not have large data holes + // spoils the effect of reducing the timeFraction late in the run + && countiter - citerlastwritten - 1 < 2.*(countiter - countwritten + 1.) / (countwritten + 1.) + // allowed time is exhausted + && timings.writedefaultfiles > options.maxTimeFractionForWriteToDefaultFiles + * (System.currentTimeMillis() - timings.start)) + return; + } + + long firsttime = System.currentTimeMillis(); + writeToFile(fileNamePrefix + "fit.dat", getDataRowFitness(), 1); + writeToFile(fileNamePrefix + "xmean.dat", getDataRowXMean(), 1); + writeToFile(fileNamePrefix + "xrecentbest.dat", getDataRowXRecentBest(), 1); + writeToFile(fileNamePrefix + "stddev.dat", getDataRowStddev(), 1); // sigma*sqrt(diag(C)) + writeToFile(fileNamePrefix + "axlen.dat", getDataRowAxlen(), 1); + timings.writedefaultfiles += System.currentTimeMillis() - firsttime; +// System.out.println(timings.writedefaultfiles + " " +// + (System.currentTimeMillis()-timings.start) + " " + opts.maxTimeFractionForWriteToDefaultFiles); + if (countiter < 3) + timings.writedefaultfiles = 0; + + ++countwritten; + citerlastwritten = countiter; + } + /** writes header lines to the default files. Could become XML if needed. + * + * @param flgAppend == 0 means overwrite files, == 1 means append to files + */ + public void writeToDefaultFilesHeaders(int flgAppend) { + writeToDefaultFilesHeaders(options.outputFileNamesPrefix, flgAppend); + } + /** + * Writes headers (column annotations) to files <prefix>fit.dat, ...xmean.dat + * ...xbest.dat, ...std.dat, ...axlen.dat, and in case the first data + * line, usually with the initial values. + * @param fileNamePrefix String for filenames created to write data */ + public void writeToDefaultFilesHeaders(String fileNamePrefix, int flgAppend) { + if (options.maxTimeFractionForWriteToDefaultFiles < 0) // overwrites force flag + return; + String s = "(randomSeed=" + seed + ", " + new Date().toString() + ")\n"; + writeToFile(fileNamePrefix + "fit.dat", + "%# iteration evaluations sigma axisratio fitness_of(bestever best median worst) mindii " + + "idxmaxSD maxSD idxminSD minSD " + + s, flgAppend); + writeToFile(fileNamePrefix + "xmean.dat", + "%# iteration evaluations sigma void void mean(1...dimension) " + s, flgAppend); + if (state == 0) + writeToFile(fileNamePrefix + "xmean.dat", getDataRowXMean(), 1); + writeToFile(fileNamePrefix + "xrecentbest.dat", + "%# iteration evaluations sigma void fitness_of_recent_best x_of_recent_best(1...dimension) " + + s, flgAppend); + writeToFile(fileNamePrefix + "stddev.dat", + "%# iteration evaluations sigma idxmaxSD idxminSD SDs=sigma*sqrt(diag(C)) " + + s, flgAppend); + if (state == 0) + writeToFile(fileNamePrefix + "stddev.dat", getDataRowStddev(), 1); + writeToFile(fileNamePrefix + "axlen.dat", + "%# iteration evaluations sigma axisratio stddevratio sort(diag(D)) (square roots of eigenvalues of C) " + + s, flgAppend); + if (state == 0) + writeToFile(fileNamePrefix + "axlen.dat", getDataRowAxlen(), 1); + } + + /** very provisional error handling. Methods of the class + * CMAEvolutionStrategy might throw the CMAException, that + * need not be catched, because it extends the "unchecked" + * RuntimeException class */ + public class CMAException extends RuntimeException { + private static final long serialVersionUID = 1L; + + CMAException(String s) { + super(s); + } + } +} + +class IntDouble implements Comparator<IntDouble> { + int i; // unique integer value, useful after sorting + double val; // double value + public IntDouble(double d, int i) { + this.val = d; + this.i = i; + } + public IntDouble(double d) { + this.val = d; + } + public IntDouble() { + } + public int compare(IntDouble o1, IntDouble o2) { + if (o1.val < o2.val) + return -1; + if (o1.val > o2.val) + return 1; + if (o1.i < o2.i) + return -1; + if (o1.i > o2.i) + return 1; + return 0; + } + + public boolean equals(IntDouble o1, IntDouble o2) { + if (o1.compare(o1, o2) == 0) // && o1.hashCode() == o2.hashCode() + return true; + return false; + } +} // IntDouble diff --git a/src/main/java/fr/inria/optimization/cmaes/CMAOptions.java b/src/main/java/fr/inria/optimization/cmaes/CMAOptions.java new file mode 100644 index 0000000000000000000000000000000000000000..ae558a0934cd0ef55f85af2796dbdefa1ae77666 --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/CMAOptions.java @@ -0,0 +1,257 @@ +package fr.inria.optimization.cmaes; + +import java.util.Properties; + +/* + Copyright 2003, 2005, 2007 Nikolaus Hansen + e-mail: hansen .AT. bionik.tu-berlin.de + hansen .AT. lri.fr + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License, version 3, + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + Last change: $Date: 2010-12-02 23:57:21 +0100 (Thu, 02 Dec 2010) $ + */ + +/** Simple container of (mostly generic) options for the + * optimization, like the maximum number of objective + * function evaluations, see class fields. No explicit setting of + * options is needed to + * initialize the CMA-ES ({@link CMAEvolutionStrategy#init()}) + * and options of the CMA-ES can be set + * and changed any time, either via a property file and the method + * {@link CMAEvolutionStrategy#readProperties()}, or new values can simply be + * assigned to the fields of the public <code>opts</code> field of + * the class <code>CMAEvolutionStrategy</code> (yeah, I know, not exactly Java style). + * + */ +public class CMAOptions implements java.io.Serializable { + // needs to be public to make sure that a using class can excess Options. + // Therefore, if not nested, needs to move into a separate file + + private static final long serialVersionUID = 2255162105325585121L; + + /** number of initial iterations with diagonal covariance matrix, where + * 1 means always. Default is + * diagonalCovarianceMatrix=0, but this will presumably change in future. + * As long as iterations<=diagonalCovarianceMatrix + * the internal time complexity is linear in the search space dimensionality + * (memory requirements remain quadratic). + */ + public long diagonalCovarianceMatrix = 0; // -1; + + /** lower bound for standard deviations (step sizes). The + * Array can be of any length. The i-th entry corresponds to + * the i-th variable. If length<dim the last entry is recycled for + * all remaining variables. Zero entries mean, naturally, no + * lower bound. <P>CAVE: there is an interference with stopTolX (and stopTolXFactor): + * if lowerStdDev is larger than stopTolX, the termination criterion + * can never be satisfied.</P> + * <p>Example: + * <pre> CMAEvolutionStrategy es = new CMAEvolutionStrategy(); + * es.options.lowerStandardDeviations = new double[]{1e-4,1e-8}; // 1e-8 for all but first variable + * </pre> + * @see #stopTolX + * @see #stopTolXFactor + * */ + public double[] lowerStandardDeviations; + /** upper bound for standard deviations (step lengths). + * Zero entries mean no upper + * bound. Be aware of the interference with option stopTolUpXFactor. + * @see #lowerStandardDeviations + * @see #stopTolUpXFactor + * */ + public double[] upperStandardDeviations; + + /** stop if function value drops below the target + * function value stopFitness. Default = <code>Double.MIN_VALUE</code> */ + public double stopFitness = Double.MIN_VALUE; + /** stop if the + * maximum function value difference of all iteration-best + * solutions of the last 10 + + * 30*N/lambda iterations + * and all solutions of the recent iteration + * become <= stopTolFun. Default = 1e-12. + * */ + public double stopTolFun = 1e-12; + /** stop if the maximum function value difference of all iteration-best + * solutions of the last 10 + + * 30*N/lambda iterations become smaller than + * stopTolFunHist. Default = 1e-13. The measured objective + * function value differences do not include repair + * penalties. */ + public double stopTolFunHist = 1e-13; // used if non-null + /** stop if search steps become smaller than stopTolX. Default = 0 */ + public double stopTolX = 0.0; + /** stop if search steps become smaller than stopTolXFactor * initial step size. + * Default = 1e-11. */ + public double stopTolXFactor = 1e-11; // used if TolX is null + /** stop if search steps become larger than stopTolUpXFactor + * * initial step size. Default = 1e3. When this termination + * criterion applies on a static objective function, the initial + * step-size was chosen far too + * small (or divergent behavior is observed). */ + public double stopTolUpXFactor = 1e3; // multiplier for initial sigma + /** stop if the number of objective function evaluations exceed stopMaxFunEvals */ + public long stopMaxFunEvals = Long.MAX_VALUE; // it is not straight forward to set a dimension dependent + // default as the user can first set stopMaxFunEvals + // and afterwards the dimension + /** stop if the number of iterations (generations) exceed stopMaxIter */ + public long stopMaxIter = Long.MAX_VALUE; + /** if true stopping message "Manual:..." is generated */ + public boolean stopnow = false; + + /** flag used by methods iterate(), whether to write output to files. + * Methods write an output file if flgWriteFile>0. + */ + + /** determines whether CMA says hello after initialization. + * @see CMAEvolutionStrategy#helloWorld() + * */ + public int verbosity = 1; + /** Output files written will have the names outputFileNamesPrefix*.dat */ + public String outputFileNamesPrefix = "outcmaes"; + /** if chosen > 0 the console output from functions <code>print...</code> is saved + * additionally into a file, by default <tt>outcmaesdisp.dat</tt> */ + public int writeDisplayToFile = 1; + + /** only for >= 1 results are always exactly reproducible, as otherwise the update of the + * eigensystem is conducted depending on time measurements, defaut is 0.2 */ + public double maxTimeFractionForEigendecomposition = 0.2; + /** default is 0.1 + */ + public double maxTimeFractionForWriteToDefaultFiles = 0.1; + + /** checks eigendecomposition mainly for debugging purpose, default is 0==no-check; + * the function checkEigenSystem requires O(N^3) operations. + */ + public int checkEigenSystem = 0; + + /** This is the only place where the reading of a new option needs to be declared + * + * @param properties + */ + void setOptions(Properties properties) { + String s; + diagonalCovarianceMatrix = getFirstToken(properties.getProperty("diagonalCovarianceMatrix"), diagonalCovarianceMatrix); + if((s = properties.getProperty("stopFitness")) != null) + stopFitness = Double.valueOf(getFirstToken(s)); + stopTolFun = getFirstToken(properties.getProperty("stopTolFun"), stopTolFun); + stopTolFunHist = getFirstToken(properties.getProperty("stopTolFunHist"), stopTolFunHist); + stopTolX = getFirstToken(properties.getProperty("stopTolX"), stopTolX); + stopTolXFactor = getFirstToken(properties.getProperty("stopTolXFactor"), stopTolXFactor); + stopTolUpXFactor = getFirstToken(properties.getProperty("stopTolUpXFactor"), stopTolUpXFactor); + stopMaxFunEvals = getFirstToken(properties.getProperty("stopMaxFunEvals"), stopMaxFunEvals); + stopMaxIter = getFirstToken(properties.getProperty("stopMaxIter"), stopMaxIter); + if ((s = properties.getProperty("upperStandardDeviations")) != null && !s.equals("")) + upperStandardDeviations = parseDouble(getAllToken(s)); + if ((s = properties.getProperty("lowerStandardDeviations")) != null && !s.equals("")) + lowerStandardDeviations = parseDouble(getAllToken(s)); + outputFileNamesPrefix = properties.getProperty("outputFileNamesPrefix", outputFileNamesPrefix).split("\\s")[0]; + maxTimeFractionForEigendecomposition = + getFirstToken(properties.getProperty("maxTimeFractionForEigendecomposition"), + maxTimeFractionForEigendecomposition); + maxTimeFractionForWriteToDefaultFiles = + getFirstToken(properties.getProperty("maxTimeFractionForWriteToDefaultFiles"), + maxTimeFractionForWriteToDefaultFiles); + stopnow = "now".equals(getFirstToken(properties.getProperty("stop"))); + writeDisplayToFile = getFirstToken(properties.getProperty("writeDisplayToFile"), writeDisplayToFile); + checkEigenSystem = getFirstToken(properties.getProperty("checkEigenSystem"), checkEigenSystem); + } + + /** Returns the double value of the first token of a string s or the default, + * if the string is null or empty. This method should become generic with respect to the + * type of second argument. + * @param s string where the first token is read from + * @param def double default value, in case the string is empty*/ + public Double getFirstToken(String s, Double def) { + if (s == null) + return def; + String[] ar = s.split("\\s+"); + if (ar[0].equals("")) + return def; + return Double.valueOf(ar[0]); + } + + /** should become generic with type argument? */ + public String getFirstToken(String s) { + if (s == null) + return ""; + String[] ar = s.split(new String("\\s+")); + return ar[0]; + } + + /** Returns the Integer value of the first token of a string s or the default, + * if the string is null or empty. This method should become generic with respect to the + * type of second argument. + * @param s string where the first token is read from + * @param def Integer default value, in case the string is empty*/ + public Integer getFirstToken(String s, Integer def) { + if (s == null) + return def; + String[] ar = s.split("\\s+"); + if (ar[0].equals("")) + return def; + return Integer.valueOf(ar[0]); + } + + // public <T> T getFirstToken(String s, T def) { + // if (s == null) + // return def; + // String[] ar = s.split("\\s+"); + // if (ar[0].equals("")) + // return def; + // return (T)(ar[0]); /* this fails */ + // } + + private String removeComments(String s) { + int i; + // remove trailing comments + i = s.indexOf("#"); + if (i >= 0) + s = s.substring(0,i); + i = s.indexOf("!"); + if (i >= 0) + s = s.substring(0,i); + i = s.indexOf("%"); + if (i >= 0) + s = s.substring(0,i); + i = s.indexOf("//"); + if (i >= 0) + s = s.substring(0,i); + return s; + } + + /** Returns def if s==null or empty, code dublicate, should become generic */ + private Long getFirstToken(String s, Long def) { + if (s == null) + return def; + String[] ar = removeComments(s).split("\\s+"); + if (ar[0].equals("")) + return def; + return Long.valueOf(ar[0]); + } + + String[] getAllToken(String s) { + // split w.r.t. white spaces regexp \s+ + return removeComments(s).split("\\s+"); + } + + double[] parseDouble(String[] ars) { + double[] ard = new double[ars.length]; + for(int i = 0; i < ars.length; ++i) { + ard[i] = Double.parseDouble(ars[i]); + } + return ard; + } + } + diff --git a/src/main/java/fr/inria/optimization/cmaes/CMAParameters.java b/src/main/java/fr/inria/optimization/cmaes/CMAParameters.java new file mode 100644 index 0000000000000000000000000000000000000000..fd0d423b640f4b4407f8ad2245b81e0919ea36ef --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/CMAParameters.java @@ -0,0 +1,508 @@ +package fr.inria.optimization.cmaes; + +/* + Copyright 2003, 2005, 2007 Nikolaus Hansen + e-mail: hansen .AT. bionik.tu-berlin.de + hansen .AT. lri.fr + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License, version 3, + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + Last change: $Date: 2010-12-02 23:57:21 +0100 (Thu, 02 Dec 2010) $ +*/ + +/** + * Interface to strategy parameters for the CMA Evolution + * Strategy, most importantly the population size lambda, while the change + * of other parameters is discouraged. + * The class CMAParameters processes the + * strategy parameters, like population size and learning rates, for + * the class {@link CMAEvolutionStrategy} where the public field <code>parameters</code> of + * type <code>CMAParameters</code> can + * be used to set the parameter values. The method {@link #supplementRemainders(int, CMAOptions)} + * supplements those parameters that were not explicitly given, + * regarding dependencies + * (eg, the parent number, mu, cannot be larger than the + * population size lambda) and does a respective consistency checking via method + * {@link #check()}. + * Parameters cannot be changed after CMAEvolutionStrategy method init() + * was called. + * <P> Example code snippet:</P> + * <PRE> + CMAEvolutionStrategy cma = new CMAEvolutionStrategy(); + cma.parameters.setPopulationSize(33); // set lambda + int mu = cma.parameters.getMu(); // will fail as mu was not set and missing + // parameters were not supplemented yet + cma.readProperties(); // read necessary initial values, might overwrite lambda + mu = cma.parameters.getMu(); // might still fail + cma.init(); // finalize initialization, supplement missing parameters + mu = cma.parameters.getMu(); // OK now + cma.parameters.setMu(4); // runtime error, parameters cannot be changed after init() + * </PRE> + * + * <P>Most commonly, the offspring population size lambda can be changed + * (increased) from its default value via setPopulationSize to improve the + * global search capability, see file CMAExample2.java. It is recommended to use the default + * values first! </P> + * + * @see CMAEvolutionStrategy#readProperties() + */ +public class CMAParameters implements java.io.Serializable { + /** + * + */ + private static final long serialVersionUID = -1305062342816588003L; + int supplemented; // after supplementation it is undecidable whether a parameter was + // explicitly set from outside, therefore another supplementation is not advisable + int locked; // lock when lambda is used to new data structures + int lambda; /* -> mu, <- N */ + int mu; /* -> weights, (lambda) */ + double mucov; /* -> ccov */ + double mueff; /* <- weights */ + double[] weights; /* <- mu, -> mueff, mucov, ccov */ + double damps; /* <- cs, maxeval, lambda */ + double cs; /* -> damp, <- N */ + double cc; /* <- N */ + double ccov; /* <- mucov, <- N, <- diagonalcov */ + double ccovsep; /* <- ccov */ + + double chiN; + + public CMAParameters() { + mucov = -1; + ccov = -1; + } + + /** + * Checks strategy parameter setting with respect to principle + * consistency. Returns a string with description of the first + * error found, otherwise an empty string "". + * */ + public String check() { + if (lambda <= 1) + return "offspring population size lambda must be greater than onem is " + lambda; + if (mu < 1) + return "parent number mu must be greater or equal to one, is " + mu; + if (mu > lambda) + return "parent number mu " + mu + " must be smaller or equal to offspring population size lambda " + lambda; + if (weights.length != mu) + return "number of recombination weights " + weights.length + " disagrees with parent number mu " + mu; + + if (cs <= 0 || cs > 1) + return "0 < cs <= 1 must hold for step-size cumulation parameter cs, is " + cs; + if (damps <= 0) + return "step-size damping parameter damps must be greater than zero, is " + damps; + if (cc <= 0 || cc > 1) + return "0 < cc <= 1 must hold for cumulation parameter cc, is " + cc; + if (mucov < 0) + return "mucov >= 0 must hold, is " + mucov; + if (ccov < 0) + return "learning parameter ccov >= 0 must hold, is " + ccov; + return ""; + } + /** get default parameter setting depending on given dimension N + * + * @param N dimension + * @return default parameter setting + * @see #getDefaults(int, int) + */ + public CMAParameters getDefaults(int N) { + if (N == 0) + error("default parameters needs dimension been set"); + + CMAParameters p = new CMAParameters(); + p.supplementRemainders(N, new CMAOptions()); + return p; + } + + /** get default parameter setting depending on dimension N and + * population size lambda. Code snippet to get, for example, the default parent + * number value mu (weighted recombination is default): + * + * <PRE> + * int default_mu_for_dimension_42 = new CMAParameters().getDefaults(42).getMu(); + * + * CMAEvolutionStrategy cma = new CMAEvolutionStrategy(42); + * int the_same_most_convenient = cma.getParameterDefaults().getMu(); + * int also_the_same = cma.getParameterDefaults(42).getMu(); + * </PRE> + * + * @param N + * @param lambda + * @return default parameter setting + * @see #getDefaults(int, int) + */ + public CMAParameters getDefaults(int N, int lambda) { + CMAParameters p = new CMAParameters(); + p.setLambda(lambda); + p.supplementRemainders(N, new CMAOptions()); + return p; + } + + /** + * Supplements all default parameter values that were not explicitly set already. + * Also checks whether the values that were already explicitly set are fine. + * @param N search space dimension + * @param opts {@link CMAOptions} where stopMaxFunEvals and + * stopMaxIter are used to set step-size damping parameter damps. This is of minor relevance. + */ + public void supplementRemainders(int N, CMAOptions opts) { + // parameters that can be zero were initialized to -1 + if (supplemented > 0) + error("defaults cannot be supplemented twice"); + if (N == 0) + error("dimension must be greater than zero"); + + supplemented = 1; + locked = 1; + + chiN = Math.sqrt(N) + * (1.0 - 1.0 / (4.0 * N) + 1.0 / (21.0 * N * N)); + + // set parameters to their default if they were not set before + if (lambda <= 0) + lambda = (int) (4.0 + 3.0 * Math.log(N)); + if (mu <= 0) + mu = (int) Math.floor(lambda/2.); + + if (weights == null) + setWeights(mu, recombinationType); + else if (weights.length == 0) + setWeights(mu, recombinationType); + + if (cs <= 0) + cs = (mueff+2) / (N+mueff+3); + + if (damps <= 0) + damps = + (1 + 2 * Math.max(0, Math.sqrt((mueff - 1.) / (N + 1.)) - 1)) + * Math.max(0.3, 1 - /* modification for short runs */ + N / (1e-6+Math.min(opts.stopMaxIter, + opts.stopMaxFunEvals/lambda))) + + cs ; /* minor increment */ + + if (cc <= 0) + cc = 4.0 / (N + 4.0); + + if (mucov < 0) + mucov = mueff; + + if (ccov < 0) { // TODO: setting should depend on gendiagonalcov + ccov = 2.0 / (N + 1.41) / (N + 1.41) / mucov + + (1 - (1.0 / mucov)) + * Math.min(1, (2 * mueff - 1) / (mueff + (N + 2) * (N + 2))); + ccovsep = Math.min(1, ccov * (N + 1.5) / 3.0); + } + + // check everything + String s = check(); + if (s == null) + ; + else if (s.equals("")) + ; + else + error(s); // if any prior setting does not work + + } // supplementRemainders + + /** + * Getter for property mu. + * + * @return Value of property mu. + * + */ + public int getMu() { + return mu; + } + + /** + * Setter for parent number mu, be aware of the recombinationType when setting mu + * + * @param mu + * New value for the number of parents mu. + * @see #setRecombination(int, CMAParameters.RecombinationType) + * @see #setRecombinationWeights(CMAParameters.RecombinationType) + */ + public void setMu(int mu) { + if (locked != 0) // needed because of recombination weights + error("parameters are locked"); + this.mu = mu; + } + + /** + * Getter for offspring population size lambda, no check, whether lambda was already set properly + * + * @return Value of lambda + * + */ + public int getLambda() { + return lambda; + } + + int flgLambdaChanged = 0; // not in use yet + /** + * Setter for offspring population size alias sample size + * alias lambda, use setPopulationSize() for outside use. + * + * @param lambda set population size + * @see #setPopulationSize() + */ + void setLambda(int lambda) { + if (locked != 0) + error("parameters cannot be set anymore"); + this.lambda = lambda; + } + /** @see #getLambda() */ + public int getPopulationSize() { + return getLambda(); + } + + /** + * Setter for offspring population size (lambda). If (only) lambda is + * set, other parameters, eg. mu and recombination weights and + * subsequently learning rates for the covariance matrix etc. are + * chosen accordingly + * + * @param lambda is the offspring population size + */ + public void setPopulationSize(int lambda) { + setLambda(lambda); + } + + public enum RecombinationType {superlinear, linear, equal}; + RecombinationType recombinationType = RecombinationType.superlinear; // otherwise null + /** + * Getter for property weights. + * + * @return Value of property weights. + * + */ + public double[] getWeights() { + return this.weights; + } + + /** + * Recombination weights can be equal, linearly + * decreasing, or super-linearly decreasing (default). The respective parameter value is + * in enum RecombinationType. + * @param recombinationType + * @see #setRecombination + * @see #setMu + */ + public void setRecombinationWeights(RecombinationType recombinationType) { + if (locked != 0) + error("parameters cannot be set anymore"); + this.recombinationType = recombinationType; + } + + /** + * Sets parent number mu and the policy for choosing the recombination weights. + * Recombination weights can be equal, linearly + * decreasing, or super-linearly decreasing (default). The respective parameter value is + * The respective parameter value is + * in enum RecombinationType. + * For equal recombination weights mu=lambda/4 is appropriate, otherwise mu=lambda/2. + * @param mu + * @param recombinationType + */ + public void setRecombination(int mu, RecombinationType recombinationType) { + if (locked != 0) + error("parameters are locked"); + this.mu = mu; + this.recombinationType = recombinationType; + } + + /** + * Setter for recombination weights + * + * @param mu is the number of parents, number of weights > 0 + */ + private void setWeights(int mu, RecombinationType recombinationType) { + double[] w = new double[mu]; + if (recombinationType == RecombinationType.equal) + for (int i = 0; i < mu; ++i) + w[i] = 1; + else if (recombinationType == RecombinationType.linear) + for (int i = 0; i < mu; ++i) + w[i] = mu - i; + else // default, seems as enums can be null + for (int i = 0; i < mu; ++i) + w[i] = (Math.log(mu + 1) - Math.log(i + 1)); + + setWeights(w); + } + + /** normalizes recombination weights vector and sets mueff **/ + protected void setWeights(double[] weights) { + assert locked == 0; + double sum = 0; + for (int i = 0; i < weights.length; ++i) + sum += weights[i]; + for (int i = 0; i < weights.length; ++i) + weights[i] /= sum; + this.weights = weights; + // setMu(weights.length); + double sum1 = 0; + double sum2 = 0; + for (int i = 0; i < mu; ++i) { + sum1 += weights[i]; + sum2 += weights[i] * weights[i]; + } + this.mueff = sum1 * sum1 / sum2; + } + + /** + * Getter for property mueff, the "variance effective selection mass". + * + * @return Value of property mueff. + * + */ + public double getMueff() { + return mueff; + } + + /** + * Getter for property mucov. mucov determines the + * mixing between rank-one and rank-mu update. For + * mucov = 1, no rank-mu updated takes place. + * + * @return Value of property mucov. + * + */ + public double getMucov() { + return mucov; + } + + /** + * Setter for mucov. + * + * @param mucov + * New value of mucov. + * @see #getMucov() + */ + public void setMucov(double mucov) { + if (locked != 0) // on the save side as mucov -> ccov, but in principle not essential + error("parameters cannot be set anymore"); + this.mucov = mucov; // can be set anytime + } + + /** + * Getter for property covariance matrix learning rate ccov + * + * @param flgdiag + * boolean, true for getting the learning rate when + * only the diagonal of the covariance matrix is updated + * @return Value of property ccov. + * + */ + public double getCcov(boolean flgdiag) { + if (flgdiag) + return ccovsep; + return ccov; + } + /** + * Getter for property covariance matrix learning rate ccov + * + * @return Value of property ccov. + * + */ + public double getCcov() { + return ccov; + } + + + /** + * Setter for covariance matrix learning rate ccov. For ccov=0 no covariance + * matrix adaptation takes place and only <EM>Cumulation Step-Size + * Adaptation (CSA)</EM> is conducted, also know as <EM>Path Length Control</EM>. + * + * @param ccov + * New value of property ccov. + * @see #getCcov() + */ + public void setCcov(double ccov) { + this.ccov = ccov; // can be set anytime, cave: switching from diagonal to full cov + } + + /** + * Getter for step-size damping damps. The damping damps + * determines the amount of step size change. + * + * @return Value of damps. + * + */ + public double getDamps() { + return damps; + } + + /** + * Setter for damps. + * + * @param damps + * New value of damps. + * @see #getDamps() + */ + public void setDamps(double damps) { + if (locked != 0) // not really necessary!? + error("parameters cannot be set anymore"); + this.damps = damps; + } + + /** + * Getter for backward time horizon parameter cc for + * distribution cumulation (for evolution path + * p<sub>c</sub>). + * + * @return Value of cc. + * + */ + public double getCc() { + return cc; + } + + /** + * Setter for cc to default value. + * + */ + public void setCc(double cc) { + this.cc = cc; + } + + /** + * Getter for cs, parameter for the backward time horizon for the cumulation for sigma. + * + * @return Value of property cs. + * + */ + public double getCs() { + return cs; + } + + /** + * Setter for cs to default value. + * @see #getCs() + */ + public void setCs(double cs) { + if (locked != 0) + error("parameters cannot be set anymore"); + this.cs = cs; + } + private void error(String s) { // somehow a relict from the C history of this code + System.out.println(" CMA-ES error: " + s); + //e.printStackTrace(); // output goes to System.err + //e.printStackTrace(System.out); // send trace to stdout + throw new CMAEvolutionStrategy().new CMAException(" CMA-ES error: " + s); // TODO this looks like a real hack + // System.exit(-1); + } + +} + diff --git a/src/main/java/fr/inria/optimization/cmaes/CMASolution.java b/src/main/java/fr/inria/optimization/cmaes/CMASolution.java new file mode 100644 index 0000000000000000000000000000000000000000..34a12d13bd55079f2816739985173699dce6baf9 --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/CMASolution.java @@ -0,0 +1,58 @@ +package fr.inria.optimization.cmaes; +import fr.inria.optimization.cmaes.ISolutionPoint; + +/** solution point in search space. Rather plain implementation of the interface ISolutionPoint. + * + * @see ISolutionPoint + * */ +public class CMASolution implements ISolutionPoint, java.io.Serializable { + /** + * + */ + private static final long serialVersionUID = 6257830429350615236L; + + public CMASolution() { + } + + public CMASolution(double[] x, double fitnessValue, long evaluation) { + // super(); // cave: default values for fields overwrite super() + this.functionValue = fitnessValue; + this.x = x.clone(); // deep copy, see http://java.sun.com/docs/books/jls/third_edition/html/arrays.html 10.7 + this.evaluation = evaluation; + } + + /* * as I do not know how to inherit clone in a decent way + * and clone even might produce shallow copies + */ + public CMASolution deepCopy() { + return new CMASolution(x, functionValue, evaluation); + } + + public CMASolution(double[] x) { + this.x = x; + } + // getter functions + public double getFitness() { return functionValue; } + public long getEvaluationNumber() { return evaluation; } + public double[] getX() { return x.clone(); } + + // setter functions + public void setFitness(double f) { functionValue = f; } + public void setEvaluationNumber(long e) { evaluation = e; } + public void setX(double[] x_in) + { + x = new double[x_in.length]; + for (int i = 0; i < x.length; ++i) + x[i] = x_in[i]; + } + + /** objective function value of x */ + private double functionValue = Double.NaN; + + /** argument to objective function to be optimized */ + private double[] x; + + /** count when the solution was evaluated */ + private long evaluation = 0; +} + diff --git a/src/main/java/fr/inria/optimization/cmaes/ISolutionPoint.java b/src/main/java/fr/inria/optimization/cmaes/ISolutionPoint.java new file mode 100644 index 0000000000000000000000000000000000000000..4fcf7289b7899b179e941d057bab1ba520dbae80 --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/ISolutionPoint.java @@ -0,0 +1,22 @@ +package fr.inria.optimization.cmaes; + + /** solution point in search space, single-objective case + * + * */ +public interface ISolutionPoint { + /** objective function value (fitness) of the search point x */ + public double getFitness(); + /** count at what evaluation number the search point x was evaluated */ + public long getEvaluationNumber(); + /** value of the point in search space, that is in the + * preimage of the objective function to be optimized */ + public double[] getX(); + + /** objective function value (fitness) of the search point x */ + public void setFitness( double fitness); // TODO better FunctionValue than Fitness ? + /** count at what evaluation number the search point x was evaluated */ + public void setEvaluationNumber( long evaluation); + /** value of the solution point in search space, the + * preimage of the objective function to be optimized */ + public void setX(double[] x); +} diff --git a/src/main/java/fr/inria/optimization/cmaes/PrintfFormat.java b/src/main/java/fr/inria/optimization/cmaes/PrintfFormat.java new file mode 100644 index 0000000000000000000000000000000000000000..038065cb6350ac400d6bda061dcfa88e1608842f --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/PrintfFormat.java @@ -0,0 +1,3091 @@ +package fr.inria.optimization.cmaes; +// +// (c) 2000 Sun Microsystems, Inc. +// ALL RIGHTS RESERVED +// +// License Grant- +// +// +// Permission to use, copy, modify, and distribute this Software and its +// documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee is +// hereby granted. +// +// This Software is provided "AS IS". All express warranties, including any +// implied warranty of merchantability, satisfactory quality, fitness for a +// particular purpose, or non-infringement, are disclaimed, except to the extent +// that such disclaimers are held to be legally invalid. +// +// You acknowledge that Software is not designed, licensed or intended for use in +// the design, construction, operation or maintenance of any nuclear facility +// ("High Risk Activities"). Sun disclaims any express or implied warranty of +// fitness for such uses. +// +// Please refer to the file http://www.sun.com/policies/trademarks/ for further +// important trademark information and to +// http://java.sun.com/nav/business/index.html for further important licensing +// information for the Java Technology. +// + + +import java.util.Enumeration; +import java.util.Vector; +import java.util.Locale; +import java.text.DecimalFormatSymbols; + +/** + * PrintfFormat allows the formatting of an array of + * objects embedded within a string. Primitive types + * must be passed using wrapper types. The formatting + * is controlled by a control string. + *<p> + * A control string is a Java string that contains a + * control specification. The control specification + * starts at the first percent sign (%) in the string, + * provided that this percent sign + *<ol> + *<li>is not escaped protected by a matching % or is + * not an escape % character, + *<li>is not at the end of the format string, and + *<li>precedes a sequence of characters that parses as + * a valid control specification. + *</ol> + *</p><p> + * A control specification usually takes the form: + *<pre> % ['-+ #0]* [0..9]* { . [0..9]* }+ + * { [hlL] }+ [idfgGoxXeEcs] + *</pre> + * There are variants of this basic form that are + * discussed below.</p> + *<p> + * The format is composed of zero or more directives + * defined as follows: + *<ul> + *<li>ordinary characters, which are simply copied to + * the output stream; + *<li>escape sequences, which represent non-graphic + * characters; and + *<li>conversion specifications, each of which + * results in the fetching of zero or more arguments. + *</ul></p> + *<p> + * The results are undefined if there are insufficient + * arguments for the format. Usually an unchecked + * exception will be thrown. If the format is + * exhausted while arguments remain, the excess + * arguments are evaluated but are otherwise ignored. + * In format strings containing the % form of + * conversion specifications, each argument in the + * argument list is used exactly once.</p> + * <p> + * Conversions can be applied to the <code>n</code>th + * argument after the format in the argument list, + * rather than to the next unused argument. In this + * case, the conversion characer % is replaced by the + * sequence %<code>n</code>$, where <code>n</code> is + * a decimal integer giving the position of the + * argument in the argument list.</p> + * <p> + * In format strings containing the %<code>n</code>$ + * form of conversion specifications, each argument + * in the argument list is used exactly once.</p> + * + *<h4>Escape Sequences</h4> + *<p> + * The following table lists escape sequences and + * associated actions on display devices capable of + * the action. + *<table> + *<tr><th align=left>Sequence</th> + * <th align=left>Name</th> + * <th align=left>Description</th></tr> + *<tr><td>\\</td><td>backlash</td><td>None. + *</td></tr> + *<tr><td>\a</td><td>alert</td><td>Attempts to alert + * the user through audible or visible + * notification. + *</td></tr> + *<tr><td>\b</td><td>backspace</td><td>Moves the + * printing position to one column before + * the current position, unless the + * current position is the start of a line. + *</td></tr> + *<tr><td>\f</td><td>form-feed</td><td>Moves the + * printing position to the initial + * printing position of the next logical + * page. + *</td></tr> + *<tr><td>\n</td><td>newline</td><td>Moves the + * printing position to the start of the + * next line. + *</td></tr> + *<tr><td>\r</td><td>carriage-return</td><td>Moves + * the printing position to the start of + * the current line. + *</td></tr> + *<tr><td>\t</td><td>tab</td><td>Moves the printing + * position to the next implementation- + * defined horizontal tab position. + *</td></tr> + *<tr><td>\v</td><td>vertical-tab</td><td>Moves the + * printing position to the start of the + * next implementation-defined vertical + * tab position. + *</td></tr> + *</table></p> + *<h4>Conversion Specifications</h4> + *<p> + * Each conversion specification is introduced by + * the percent sign character (%). After the character + * %, the following appear in sequence:</p> + *<p> + * Zero or more flags (in any order), which modify the + * meaning of the conversion specification.</p> + *<p> + * An optional minimum field width. If the converted + * value has fewer characters than the field width, it + * will be padded with spaces by default on the left; + * t will be padded on the right, if the left- + * adjustment flag (-), described below, is given to + * the field width. The field width takes the form + * of a decimal integer. If the conversion character + * is s, the field width is the the minimum number of + * characters to be printed.</p> + *<p> + * An optional precision that gives the minumum number + * of digits to appear for the d, i, o, x or X + * conversions (the field is padded with leading + * zeros); the number of digits to appear after the + * radix character for the e, E, and f conversions, + * the maximum number of significant digits for the g + * and G conversions; or the maximum number of + * characters to be written from a string is s and S + * conversions. The precision takes the form of an + * optional decimal digit string, where a null digit + * string is treated as 0. If a precision appears + * with a c conversion character the precision is + * ignored. + * </p> + *<p> + * An optional h specifies that a following d, i, o, + * x, or X conversion character applies to a type + * short argument (the argument will be promoted + * according to the integral promotions and its value + * converted to type short before printing).</p> + *<p> + * An optional l (ell) specifies that a following + * d, i, o, x, or X conversion character applies to a + * type long argument.</p> + *<p> + * A field width or precision may be indicated by an + * asterisk (*) instead of a digit string. In this + * case, an integer argument supplised the field width + * precision. The argument that is actually converted + * is not fetched until the conversion letter is seen, + * so the the arguments specifying field width or + * precision must appear before the argument (if any) + * to be converted. If the precision argument is + * negative, it will be changed to zero. A negative + * field width argument is taken as a - flag, followed + * by a positive field width.</p> + * <p> + * In format strings containing the %<code>n</code>$ + * form of a conversion specification, a field width + * or precision may be indicated by the sequence + * *<code>m</code>$, where m is a decimal integer + * giving the position in the argument list (after the + * format argument) of an integer argument containing + * the field width or precision.</p> + * <p> + * The format can contain either numbered argument + * specifications (that is, %<code>n</code>$ and + * *<code>m</code>$), or unnumbered argument + * specifications (that is % and *), but normally not + * both. The only exception to this is that %% can + * be mixed with the %<code>n</code>$ form. The + * results of mixing numbered and unnumbered argument + * specifications in a format string are undefined.</p> + * + *<h4>Flag Characters</h4> + *<p> + * The flags and their meanings are:</p> + *<dl> + * <dt>'<dd> integer portion of the result of a + * decimal conversion (%i, %d, %f, %g, or %G) will + * be formatted with thousands' grouping + * characters. For other conversions the flag + * is ignored. The non-monetary grouping + * character is used. + * <dt>-<dd> result of the conversion is left-justified + * within the field. (It will be right-justified + * if this flag is not specified).</td></tr> + * <dt>+<dd> result of a signed conversion always + * begins with a sign (+ or -). (It will begin + * with a sign only when a negative value is + * converted if this flag is not specified.) + * <dt><space><dd> If the first character of a + * signed conversion is not a sign, a space + * character will be placed before the result. + * This means that if the space character and + + * flags both appear, the space flag will be + * ignored. + * <dt>#<dd> value is to be converted to an alternative + * form. For c, d, i, and s conversions, the flag + * has no effect. For o conversion, it increases + * the precision to force the first digit of the + * result to be a zero. For x or X conversion, a + * non-zero result has 0x or 0X prefixed to it, + * respectively. For e, E, f, g, and G + * conversions, the result always contains a radix + * character, even if no digits follow the radix + * character (normally, a decimal point appears in + * the result of these conversions only if a digit + * follows it). For g and G conversions, trailing + * zeros will not be removed from the result as + * they normally are. + * <dt>0<dd> d, i, o, x, X, e, E, f, g, and G + * conversions, leading zeros (following any + * indication of sign or base) are used to pad to + * the field width; no space padding is + * performed. If the 0 and - flags both appear, + * the 0 flag is ignored. For d, i, o, x, and X + * conversions, if a precision is specified, the + * 0 flag will be ignored. For c conversions, + * the flag is ignored. + *</dl> + * + *<h4>Conversion Characters</h4> + *<p> + * Each conversion character results in fetching zero + * or more arguments. The results are undefined if + * there are insufficient arguments for the format. + * Usually, an unchecked exception will be thrown. + * If the format is exhausted while arguments remain, + * the excess arguments are ignored.</p> + * + *<p> + * The conversion characters and their meanings are: + *</p> + *<dl> + * <dt>d,i<dd>The int argument is converted to a + * signed decimal in the style [-]dddd. The + * precision specifies the minimum number of + * digits to appear; if the value being + * converted can be represented in fewer + * digits, it will be expanded with leading + * zeros. The default precision is 1. The + * result of converting 0 with an explicit + * precision of 0 is no characters. + * <dt>o<dd> The int argument is converted to unsigned + * octal format in the style ddddd. The + * precision specifies the minimum number of + * digits to appear; if the value being + * converted can be represented in fewer + * digits, it will be expanded with leading + * zeros. The default precision is 1. The + * result of converting 0 with an explicit + * precision of 0 is no characters. + * <dt>x<dd> The int argument is converted to unsigned + * hexadecimal format in the style dddd; the + * letters abcdef are used. The precision + * specifies the minimum numberof digits to + * appear; if the value being converted can be + * represented in fewer digits, it will be + * expanded with leading zeros. The default + * precision is 1. The result of converting 0 + * with an explicit precision of 0 is no + * characters. + * <dt>X<dd> Behaves the same as the x conversion + * character except that letters ABCDEF are + * used instead of abcdef. + * <dt>f<dd> The floating point number argument is + * written in decimal notation in the style + * [-]ddd.ddd, where the number of digits after + * the radix character (shown here as a decimal + * point) is equal to the precision + * specification. A Locale is used to determine + * the radix character to use in this format. + * If the precision is omitted from the + * argument, six digits are written after the + * radix character; if the precision is + * explicitly 0 and the # flag is not specified, + * no radix character appears. If a radix + * character appears, at least 1 digit appears + * before it. The value is rounded to the + * appropriate number of digits. + * <dt>e,E<dd>The floating point number argument is + * written in the style [-]d.ddde{+-}dd + * (the symbols {+-} indicate either a plus or + * minus sign), where there is one digit before + * the radix character (shown here as a decimal + * point) and the number of digits after it is + * equal to the precision. A Locale is used to + * determine the radix character to use in this + * format. When the precision is missing, six + * digits are written after the radix character; + * if the precision is 0 and the # flag is not + * specified, no radix character appears. The + * E conversion will produce a number with E + * instead of e introducing the exponent. The + * exponent always contains at least two digits. + * However, if the value to be written requires + * an exponent greater than two digits, + * additional exponent digits are written as + * necessary. The value is rounded to the + * appropriate number of digits. + * <dt>g,G<dd>The floating point number argument is + * written in style f or e (or in sytle E in the + * case of a G conversion character), with the + * precision specifying the number of + * significant digits. If the precision is + * zero, it is taken as one. The style used + * depends on the value converted: style e + * (or E) will be used only if the exponent + * resulting from the conversion is less than + * -4 or greater than or equal to the precision. + * Trailing zeros are removed from the result. + * A radix character appears only if it is + * followed by a digit. + * <dt>c,C<dd>The integer argument is converted to a + * char and the result is written. + * + * <dt>s,S<dd>The argument is taken to be a string and + * bytes from the string are written until the + * end of the string or the number of bytes + * indicated by the precision specification of + * the argument is reached. If the precision + * is omitted from the argument, it is taken to + * be infinite, so all characters up to the end + * of the string are written. + * <dt>%<dd>Write a % character; no argument is + * converted. + *</dl> + *<p> + * If a conversion specification does not match one of + * the above forms, an IllegalArgumentException is + * thrown and the instance of PrintfFormat is not + * created.</p> + *<p> + * If a floating point value is the internal + * representation for infinity, the output is + * [+]Infinity, where Infinity is either Infinity or + * Inf, depending on the desired output string length. + * Printing of the sign follows the rules described + * above.</p> + *<p> + * If a floating point value is the internal + * representation for "not-a-number," the output is + * [+]NaN. Printing of the sign follows the rules + * described above.</p> + *<p> + * In no case does a non-existent or small field width + * cause truncation of a field; if the result of a + * conversion is wider than the field width, the field + * is simply expanded to contain the conversion result. + *</p> + *<p> + * The behavior is like printf. One exception is that + * the minimum number of exponent digits is 3 instead + * of 2 for e and E formats when the optional L is used + * before the e, E, g, or G conversion character. The + * optional L does not imply conversion to a long long + * double. </p> + * <p> + * The biggest divergence from the C printf + * specification is in the use of 16 bit characters. + * This allows the handling of characters beyond the + * small ASCII character set and allows the utility to + * interoperate correctly with the rest of the Java + * runtime environment.</p> + *<p> + * Omissions from the C printf specification are + * numerous. All the known omissions are present + * because Java never uses bytes to represent + * characters and does not have pointers:</p> + *<ul> + * <li>%c is the same as %C. + * <li>%s is the same as %S. + * <li>u, p, and n conversion characters. + * <li>%ws format. + * <li>h modifier applied to an n conversion character. + * <li>l (ell) modifier applied to the c, n, or s + * conversion characters. + * <li>ll (ell ell) modifier to d, i, o, u, x, or X + * conversion characters. + * <li>ll (ell ell) modifier to an n conversion + * character. + * <li>c, C, d,i,o,u,x, and X conversion characters + * apply to Byte, Character, Short, Integer, Long + * types. + * <li>f, e, E, g, and G conversion characters apply + * to Float and Double types. + * <li>s and S conversion characters apply to String + * types. + * <li>All other reference types can be formatted + * using the s or S conversion characters only. + *</ul> + * <p> + * Most of this specification is quoted from the Unix + * man page for the sprintf utility.</p> + * + * @author Allan Jacobs + * @version 1 + * Release 1: Initial release. + * Release 2: Asterisk field widths and precisions + * %n$ and *m$ + * Bug fixes + * g format fix (2 digits in e form corrupt) + * rounding in f format implemented + * round up when digit not printed is 5 + * formatting of -0.0f + * round up/down when last digits are 50000... + */ +public class PrintfFormat { + /** + * Constructs an array of control specifications + * possibly preceded, separated, or followed by + * ordinary strings. Control strings begin with + * unpaired percent signs. A pair of successive + * percent signs designates a single percent sign in + * the format. + * @param fmtArg Control string. + * @exception IllegalArgumentException if the control + * string is null, zero length, or otherwise + * malformed. + */ + public PrintfFormat(String fmtArg) + throws IllegalArgumentException { + this(Locale.getDefault(),fmtArg); + } + /** + * Constructs an array of control specifications + * possibly preceded, separated, or followed by + * ordinary strings. Control strings begin with + * unpaired percent signs. A pair of successive + * percent signs designates a single percent sign in + * the format. + * @param fmtArg Control string. + * @exception IllegalArgumentException if the control + * string is null, zero length, or otherwise + * malformed. + */ + public PrintfFormat(Locale locale,String fmtArg) + throws IllegalArgumentException { + dfs = new DecimalFormatSymbols(locale); + int ePos=0; + ConversionSpecification sFmt=null; + String unCS = this.nonControl(fmtArg,0); + if (unCS!=null) { + sFmt = new ConversionSpecification(); + sFmt.setLiteral(unCS); + vFmt.addElement(sFmt); + } + while(cPos!=-1 && cPos<fmtArg.length()) { + for (ePos=cPos+1; ePos<fmtArg.length(); + ePos++) { + char c=0; + c = fmtArg.charAt(ePos); + if (c == 'i') break; + if (c == 'd') break; + if (c == 'f') break; + if (c == 'g') break; + if (c == 'G') break; + if (c == 'o') break; + if (c == 'x') break; + if (c == 'X') break; + if (c == 'e') break; + if (c == 'E') break; + if (c == 'c') break; + if (c == 's') break; + if (c == '%') break; + } + ePos=Math.min(ePos+1,fmtArg.length()); + sFmt = new ConversionSpecification( + fmtArg.substring(cPos,ePos)); + vFmt.addElement(sFmt); + unCS = this.nonControl(fmtArg,ePos); + if (unCS!=null) { + sFmt = new ConversionSpecification(); + sFmt.setLiteral(unCS); + vFmt.addElement(sFmt); + } + } + } + /** + * Return a substring starting at + * <code>start</code> and ending at either the end + * of the String <code>s</code>, the next unpaired + * percent sign, or at the end of the String if the + * last character is a percent sign. + * @param s Control string. + * @param start Position in the string + * <code>s</code> to begin looking for the start + * of a control string. + * @return the substring from the start position + * to the beginning of the control string. + */ + private String nonControl(String s,int start) { + String ret=""; + cPos=s.indexOf("%",start); + if (cPos==-1) cPos=s.length(); + return s.substring(start,cPos); + } + /** + * Format an array of objects. Byte, Short, + * Integer, Long, Float, Double, and Character + * arguments are treated as wrappers for primitive + * types. + * @param o The array of objects to format. + * @return The formatted String. + */ + public String sprintf(Object[] o) { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + int i=0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else { + if (cs.isPositionalSpecification()) { + i=cs.getArgumentPosition()-1; + if (cs.isPositionalFieldWidth()) { + int ifw=cs.getArgumentPositionForFieldWidth()-1; + cs.setFieldWidthWithArg(((Integer)o[ifw]).intValue()); + } + if (cs.isPositionalPrecision()) { + int ipr=cs.getArgumentPositionForPrecision()-1; + cs.setPrecisionWithArg(((Integer)o[ipr]).intValue()); + } + } + else { + if (cs.isVariableFieldWidth()) { + cs.setFieldWidthWithArg(((Integer)o[i]).intValue()); + i++; + } + if (cs.isVariablePrecision()) { + cs.setPrecisionWithArg(((Integer)o[i]).intValue()); + i++; + } + } + if (o[i] instanceof Byte) + sb.append(cs.internalsprintf( + ((Byte)o[i]).byteValue())); + else if (o[i] instanceof Short) + sb.append(cs.internalsprintf( + ((Short)o[i]).shortValue())); + else if (o[i] instanceof Integer) + sb.append(cs.internalsprintf( + ((Integer)o[i]).intValue())); + else if (o[i] instanceof Long) + sb.append(cs.internalsprintf( + ((Long)o[i]).longValue())); + else if (o[i] instanceof Float) + sb.append(cs.internalsprintf( + ((Float)o[i]).floatValue())); + else if (o[i] instanceof Double) + sb.append(cs.internalsprintf( + ((Double)o[i]).doubleValue())); + else if (o[i] instanceof Character) + sb.append(cs.internalsprintf( + ((Character)o[i]).charValue())); + else if (o[i] instanceof String) + sb.append(cs.internalsprintf( + (String)o[i])); + else + sb.append(cs.internalsprintf( + o[i])); + if (!cs.isPositionalSpecification()) + i++; + } + } + return sb.toString(); + } + /** + * Format nothing. Just use the control string. + * @return the formatted String. + */ + public String sprintf() { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + } + return sb.toString(); + } + /** + * Format an int. + * @param x The int to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, G, s, + * or S. + */ + public String sprintf(int x) + throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format an long. + * @param x The long to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, G, s, + * or S. + */ + public String sprintf(long x) + throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format a double. + * @param x The double to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is c, C, s, S, + * d, d, x, X, or o. + */ + public String sprintf(double x) + throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format a String. + * @param x The String to format. + * @return The formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + public String sprintf(String x) + throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else sb.append(cs.internalsprintf(x)); + } + return sb.toString(); + } + /** + * Format an Object. Convert wrapper types to + * their primitive equivalents and call the + * appropriate internal formatting method. Convert + * Strings using an internal formatting method for + * Strings. Otherwise use the default formatter + * (use toString). + * @param x the Object to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is inappropriate for + * formatting an unwrapped value. + */ + public String sprintf(Object x) + throws IllegalArgumentException { + Enumeration e = vFmt.elements(); + ConversionSpecification cs = null; + char c = 0; + StringBuffer sb=new StringBuffer(); + while (e.hasMoreElements()) { + cs = (ConversionSpecification) + e.nextElement(); + c = cs.getConversionCharacter(); + if (c=='\0') sb.append(cs.getLiteral()); + else if (c=='%') sb.append("%"); + else { + if (x instanceof Byte) + sb.append(cs.internalsprintf( + ((Byte)x).byteValue())); + else if (x instanceof Short) + sb.append(cs.internalsprintf( + ((Short)x).shortValue())); + else if (x instanceof Integer) + sb.append(cs.internalsprintf( + ((Integer)x).intValue())); + else if (x instanceof Long) + sb.append(cs.internalsprintf( + ((Long)x).longValue())); + else if (x instanceof Float) + sb.append(cs.internalsprintf( + ((Float)x).floatValue())); + else if (x instanceof Double) + sb.append(cs.internalsprintf( + ((Double)x).doubleValue())); + else if (x instanceof Character) + sb.append(cs.internalsprintf( + ((Character)x).charValue())); + else if (x instanceof String) + sb.append(cs.internalsprintf( + (String)x)); + else + sb.append(cs.internalsprintf(x)); + } + } + return sb.toString(); + } + /** + *<p> + * ConversionSpecification allows the formatting of + * a single primitive or object embedded within a + * string. The formatting is controlled by a + * format string. Only one Java primitive or + * object can be formatted at a time. + *<p> + * A format string is a Java string that contains + * a control string. The control string starts at + * the first percent sign (%) in the string, + * provided that this percent sign + *<ol> + *<li>is not escaped protected by a matching % or + * is not an escape % character, + *<li>is not at the end of the format string, and + *<li>precedes a sequence of characters that parses + * as a valid control string. + *</ol> + *<p> + * A control string takes the form: + *<pre> % ['-+ #0]* [0..9]* { . [0..9]* }+ + * { [hlL] }+ [idfgGoxXeEcs] + *</pre> + *<p> + * The behavior is like printf. One (hopefully the + * only) exception is that the minimum number of + * exponent digits is 3 instead of 2 for e and E + * formats when the optional L is used before the + * e, E, g, or G conversion character. The + * optional L does not imply conversion to a long + * long double. + */ + private class ConversionSpecification { + /** + * Constructor. Used to prepare an instance + * to hold a literal, not a control string. + */ + ConversionSpecification() { } + /** + * Constructor for a conversion specification. + * The argument must begin with a % and end + * with the conversion character for the + * conversion specification. + * @param fmtArg String specifying the + * conversion specification. + * @exception IllegalArgumentException if the + * input string is null, zero length, or + * otherwise malformed. + */ + ConversionSpecification(String fmtArg) + throws IllegalArgumentException { + if (fmtArg==null) + throw new NullPointerException(); + if (fmtArg.length()==0) + throw new IllegalArgumentException( + "Control strings must have positive"+ + " lengths."); + if (fmtArg.charAt(0)=='%') { + fmt = fmtArg; + pos=1; + setArgPosition(); + setFlagCharacters(); + setFieldWidth(); + setPrecision(); + setOptionalHL(); + if (setConversionCharacter()) { + if (pos==fmtArg.length()) { + if(leadingZeros&&leftJustify) + leadingZeros=false; + if(precisionSet&&leadingZeros){ + if(conversionCharacter=='d' + ||conversionCharacter=='i' + ||conversionCharacter=='o' + ||conversionCharacter=='x') + { + leadingZeros=false; + } + } + } + else + throw new IllegalArgumentException( + "Malformed conversion specification="+ + fmtArg); + } + else + throw new IllegalArgumentException( + "Malformed conversion specification="+ + fmtArg); + } + else + throw new IllegalArgumentException( + "Control strings must begin with %."); + } + /** + * Set the String for this instance. + * @param s the String to store. + */ + void setLiteral(String s) { + fmt = s; + } + /** + * Get the String for this instance. Translate + * any escape sequences. + * + * @return s the stored String. + */ + String getLiteral() { + StringBuffer sb=new StringBuffer(); + int i=0; + while (i<fmt.length()) { + if (fmt.charAt(i)=='\\') { + i++; + if (i<fmt.length()) { + char c=fmt.charAt(i); + switch(c) { + case 'a': + sb.append((char)0x07); + break; + case 'b': + sb.append('\b'); + break; + case 'f': + sb.append('\f'); + break; + case 'n': + sb.append(System.getProperty("line.separator")); + break; + case 'r': + sb.append('\r'); + break; + case 't': + sb.append('\t'); + break; + case 'v': + sb.append((char)0x0b); + break; + case '\\': + sb.append('\\'); + break; + } + i++; + } + else + sb.append('\\'); + } + else + i++; + } + return fmt; + } + /** + * Get the conversion character that tells what + * type of control character this instance has. + * + * @return the conversion character. + */ + char getConversionCharacter() { + return conversionCharacter; + } + /** + * Check whether the specifier has a variable + * field width that is going to be set by an + * argument. + * @return <code>true</code> if the conversion + * uses an * field width; otherwise + * <code>false</code>. + */ + boolean isVariableFieldWidth() { + return variableFieldWidth; + } + /** + * Set the field width with an argument. A + * negative field width is taken as a - flag + * followed by a positive field width. + * @param fw the field width. + */ + void setFieldWidthWithArg(int fw) { + if (fw<0) leftJustify = true; + fieldWidthSet = true; + fieldWidth = Math.abs(fw); + } + /** + * Check whether the specifier has a variable + * precision that is going to be set by an + * argument. + * @return <code>true</code> if the conversion + * uses an * precision; otherwise + * <code>false</code>. + */ + boolean isVariablePrecision() { + return variablePrecision; + } + /** + * Set the precision with an argument. A + * negative precision will be changed to zero. + * @param pr the precision. + */ + void setPrecisionWithArg(int pr) { + precisionSet = true; + precision = Math.max(pr,0); + } + /** + * Format an int argument using this conversion + * specification. + * @param s the int to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, or G. + */ + String internalsprintf(int s) + throws IllegalArgumentException { + String s2 = ""; + switch(conversionCharacter) { + case 'd': + case 'i': + if (optionalh) + s2 = printDFormat((short)s); + else if (optionall) + s2 = printDFormat((long)s); + else + s2 = printDFormat(s); + break; + case 'x': + case 'X': + if (optionalh) + s2 = printXFormat((short)s); + else if (optionall) + s2 = printXFormat((long)s); + else + s2 = printXFormat(s); + break; + case 'o': + if (optionalh) + s2 = printOFormat((short)s); + else if (optionall) + s2 = printOFormat((long)s); + else + s2 = printOFormat(s); + break; + case 'c': + case 'C': + s2 = printCFormat((char)s); + break; + default: + throw new IllegalArgumentException( + "Cannot format a int with a format using a "+ + conversionCharacter+ + " conversion character."); + } + return s2; + } + /** + * Format a long argument using this conversion + * specification. + * @param s the long to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is f, e, E, g, or G. + */ + String internalsprintf(long s) + throws IllegalArgumentException { + String s2 = ""; + switch(conversionCharacter) { + case 'd': + case 'i': + if (optionalh) + s2 = printDFormat((short)s); + else if (optionall) + s2 = printDFormat(s); + else + s2 = printDFormat((int)s); + break; + case 'x': + case 'X': + if (optionalh) + s2 = printXFormat((short)s); + else if (optionall) + s2 = printXFormat(s); + else + s2 = printXFormat((int)s); + break; + case 'o': + if (optionalh) + s2 = printOFormat((short)s); + else if (optionall) + s2 = printOFormat(s); + else + s2 = printOFormat((int)s); + break; + case 'c': + case 'C': + s2 = printCFormat((char)s); + break; + default: + throw new IllegalArgumentException( + "Cannot format a long with a format using a "+ + conversionCharacter+" conversion character."); + } + return s2; + } + /** + * Format a double argument using this conversion + * specification. + * @param s the double to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is c, C, s, S, i, d, + * x, X, or o. + */ + String internalsprintf(double s) + throws IllegalArgumentException { + String s2 = ""; + switch(conversionCharacter) { + case 'f': + s2 = printFFormat(s); + break; + case 'E': + case 'e': + s2 = printEFormat(s); + break; + case 'G': + case 'g': + s2 = printGFormat(s); + break; + default: + throw new IllegalArgumentException("Cannot "+ + "format a double with a format using a "+ + conversionCharacter+" conversion character."); + } + return s2; + } + /** + * Format a String argument using this conversion + * specification. + * @param s the String to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + String internalsprintf(String s) + throws IllegalArgumentException { + String s2 = ""; + if(conversionCharacter=='s' + || conversionCharacter=='S') + s2 = printSFormat(s); + else + throw new IllegalArgumentException("Cannot "+ + "format a String with a format using a "+ + conversionCharacter+" conversion character."); + return s2; + } + /** + * Format an Object argument using this conversion + * specification. + * @param s the Object to format. + * @return the formatted String. + * @exception IllegalArgumentException if the + * conversion character is neither s nor S. + */ + String internalsprintf(Object s) { + String s2 = ""; + if(conversionCharacter=='s' + || conversionCharacter=='S') + s2 = printSFormat(s.toString()); + else + throw new IllegalArgumentException( + "Cannot format a String with a format using"+ + " a "+conversionCharacter+ + " conversion character."); + return s2; + } + /** + * For f format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both + * a '+' and a ' ' are specified, the blank flag + * is ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the number of digits + * to appear after the radix character. Padding is + * with trailing 0s. + */ + private char[] fFormatDigits(double x) { + // int defaultDigits=6; + String sx,sxOut; + int i,j,k; + int n1In,n2In; + int expon=0; + boolean minusSign=false; + if (x>0.0) + sx = Double.toString(x); + else if (x<0.0) { + sx = Double.toString(-x); + minusSign=true; + } + else { + sx = Double.toString(x); + if (sx.charAt(0)=='-') { + minusSign=true; + sx=sx.substring(1); + } + } + int ePos = sx.indexOf('E'); + int rPos = sx.indexOf('.'); + if (rPos!=-1) n1In=rPos; + else if (ePos!=-1) n1In=ePos; + else n1In=sx.length(); + if (rPos!=-1) { + if (ePos!=-1) n2In = ePos-rPos-1; + else n2In = sx.length()-rPos-1; + } + else + n2In = 0; + if (ePos!=-1) { + int ie=ePos+1; + expon=0; + if (sx.charAt(ie)=='-') { + for (++ie; ie<sx.length(); ie++) + if (sx.charAt(ie)!='0') break; + if (ie<sx.length()) + expon=-Integer.parseInt(sx.substring(ie)); + } + else { + if (sx.charAt(ie)=='+') ++ie; + for (; ie<sx.length(); ie++) + if (sx.charAt(ie)!='0') break; + if (ie<sx.length()) + expon=Integer.parseInt(sx.substring(ie)); + } + } + int p; + if (precisionSet) p = precision; + else p = defaultDigits-1; + char[] ca1 = sx.toCharArray(); + char[] ca2 = new char[n1In+n2In]; + char[] ca3,ca4,ca5; + for (j=0; j<n1In; j++) + ca2[j] = ca1[j]; + i = j+1; + for (k=0; k<n2In; j++,i++,k++) + ca2[j] = ca1[i]; + if (n1In+expon<=0) { + ca3 = new char[-expon+n2In]; + for (j=0,k=0; k<(-n1In-expon); k++,j++) + ca3[j]='0'; + for (i=0; i<(n1In+n2In); i++,j++) + ca3[j]=ca2[i]; + } + else + ca3 = ca2; + boolean carry=false; + if (p<-expon+n2In) { + if (expon<0) i = p; + else i = p+n1In; + carry=checkForCarry(ca3,i); + if (carry) + carry=startSymbolicCarry(ca3,i-1,0); + } + if (n1In+expon<=0) { + ca4 = new char[2+p]; + if (!carry) ca4[0]='0'; + else ca4[0]='1'; + if(alternateForm||!precisionSet||precision!=0){ + ca4[1]='.'; + for(i=0,j=2;i<Math.min(p,ca3.length);i++,j++) + ca4[j]=ca3[i]; + for (; j<ca4.length; j++) ca4[j]='0'; + } + } + else { + if (!carry) { + if(alternateForm||!precisionSet + ||precision!=0) + ca4 = new char[n1In+expon+p+1]; + else + ca4 = new char[n1In+expon]; + j=0; + } + else { + if(alternateForm||!precisionSet + ||precision!=0) + ca4 = new char[n1In+expon+p+2]; + else + ca4 = new char[n1In+expon+1]; + ca4[0]='1'; + j=1; + } + for (i=0; i<Math.min(n1In+expon,ca3.length); i++,j++) + ca4[j]=ca3[i]; + for (; i<n1In+expon; i++,j++) + ca4[j]='0'; + if(alternateForm||!precisionSet||precision!=0){ + ca4[j]='.'; j++; + for (k=0; i<ca3.length && k<p; i++,j++,k++) + ca4[j]=ca3[i]; + for (; j<ca4.length; j++) ca4[j]='0'; + } + } + int nZeros=0; + if (!leftJustify && leadingZeros) { + int xThousands=0; + if (thousands) { + int xlead=0; + if (ca4[0]=='+'||ca4[0]=='-'||ca4[0]==' ') + xlead=1; + int xdp=xlead; + for (; xdp<ca4.length; xdp++) + if (ca4[xdp]=='.') break; + xThousands=(xdp-xlead)/3; + } + if (fieldWidthSet) + nZeros = fieldWidth-ca4.length; + if ((!minusSign&&(leadingSign||leadingSpace))||minusSign) + nZeros--; + nZeros-=xThousands; + if (nZeros<0) nZeros=0; + } + j=0; + if ((!minusSign&&(leadingSign||leadingSpace))||minusSign) { + ca5 = new char[ca4.length+nZeros+1]; + j++; + } + else + ca5 = new char[ca4.length+nZeros]; + if (!minusSign) { + if (leadingSign) ca5[0]='+'; + if (leadingSpace) ca5[0]=' '; + } + else + ca5[0]='-'; + for (i=0; i<nZeros; i++,j++) + ca5[j]='0'; + for (i=0; i<ca4.length; i++,j++) ca5[j]=ca4[i]; + + int lead=0; + if (ca5[0]=='+'||ca5[0]=='-'||ca5[0]==' ') + lead=1; + int dp=lead; + for (; dp<ca5.length; dp++) + if (ca5[dp]=='.') break; + int nThousands=(dp-lead)/3; + // Localize the decimal point. + if (dp<ca5.length) + ca5[dp]=dfs.getDecimalSeparator(); + char[] ca6 = ca5; + if (thousands && nThousands>0) { + ca6 = new char[ca5.length+nThousands+lead]; + ca6[0]=ca5[0]; + for (i=lead,k=lead; i<dp; i++) { + if (i>0 && (dp-i)%3==0) { + // ca6[k]=','; + ca6[k]=dfs.getGroupingSeparator(); + ca6[k+1]=ca5[i]; + k+=2; + } + else { + ca6[k]=ca5[i]; k++; + } + } + for (; i<ca5.length; i++,k++) { + ca6[k]=ca5[i]; + } + } + return ca6; + } + /** + * An intermediate routine on the way to creating + * an f format String. The method decides whether + * the input double value is an infinity, + * not-a-number, or a finite double and formats + * each type of input appropriately. + * @param x the double value to be formatted. + * @return the converted double value. + */ + private String fFormatString(double x) { + boolean noDigits=false; + char[] ca6,ca7; + if (Double.isInfinite(x)) { + if (x==Double.POSITIVE_INFINITY) { + if (leadingSign) ca6 = "+Inf".toCharArray(); + else if (leadingSpace) + ca6 = " Inf".toCharArray(); + else ca6 = "Inf".toCharArray(); + } + else + ca6 = "-Inf".toCharArray(); + noDigits = true; + } + else if (Double.isNaN(x)) { + if (leadingSign) ca6 = "+NaN".toCharArray(); + else if (leadingSpace) + ca6 = " NaN".toCharArray(); + else ca6 = "NaN".toCharArray(); + noDigits = true; + } + else + ca6 = fFormatDigits(x); + ca7 = applyFloatPadding(ca6,false); + return new String(ca7); + } + /** + * For e format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear after the radix character. + * Padding is with trailing 0s. + * + * The behavior is like printf. One (hopefully the + * only) exception is that the minimum number of + * exponent digits is 3 instead of 2 for e and E + * formats when the optional L is used before the + * e, E, g, or G conversion character. The optional + * L does not imply conversion to a long long + * double. + */ + private char[] eFormatDigits(double x,char eChar) { + char[] ca1,ca2,ca3; + // int defaultDigits=6; + String sx,sxOut; + int i,j,k,p; + int n1In,n2In; + int expon=0; + int ePos,rPos,eSize; + boolean minusSign=false; + if (x>0.0) + sx = Double.toString(x); + else if (x<0.0) { + sx = Double.toString(-x); + minusSign=true; + } + else { + sx = Double.toString(x); + if (sx.charAt(0)=='-') { + minusSign=true; + sx=sx.substring(1); + } + } + ePos = sx.indexOf('E'); + if (ePos==-1) ePos = sx.indexOf('e'); + rPos = sx.indexOf('.'); + if (rPos!=-1) n1In=rPos; + else if (ePos!=-1) n1In=ePos; + else n1In=sx.length(); + if (rPos!=-1) { + if (ePos!=-1) n2In = ePos-rPos-1; + else n2In = sx.length()-rPos-1; + } + else + n2In = 0; + if (ePos!=-1) { + int ie=ePos+1; + expon=0; + if (sx.charAt(ie)=='-') { + for (++ie; ie<sx.length(); ie++) + if (sx.charAt(ie)!='0') break; + if (ie<sx.length()) + expon=-Integer.parseInt(sx.substring(ie)); + } + else { + if (sx.charAt(ie)=='+') ++ie; + for (; ie<sx.length(); ie++) + if (sx.charAt(ie)!='0') break; + if (ie<sx.length()) + expon=Integer.parseInt(sx.substring(ie)); + } + } + if (rPos!=-1) expon += rPos-1; + if (precisionSet) p = precision; + else p = defaultDigits-1; + if (rPos!=-1 && ePos!=-1) + ca1=(sx.substring(0,rPos)+ + sx.substring(rPos+1,ePos)).toCharArray(); + else if (rPos!=-1) + ca1 = (sx.substring(0,rPos)+ + sx.substring(rPos+1)).toCharArray(); + else if (ePos!=-1) + ca1 = sx.substring(0,ePos).toCharArray(); + else + ca1 = sx.toCharArray(); + boolean carry=false; + int i0=0; + if (ca1[0]!='0') + i0 = 0; + else + for (i0=0; i0<ca1.length; i0++) + if (ca1[i0]!='0') break; + if (i0+p<ca1.length-1) { + carry=checkForCarry(ca1,i0+p+1); + if (carry) + carry = startSymbolicCarry(ca1,i0+p,i0); + if (carry) { + ca2 = new char[i0+p+1]; + ca2[i0]='1'; + for (j=0; j<i0; j++) ca2[j]='0'; + for (i=i0,j=i0+1; j<p+1; i++,j++) + ca2[j] = ca1[i]; + expon++; + ca1 = ca2; + } + } + if (Math.abs(expon)<100 && !optionalL) eSize=4; + else eSize=5; + if (alternateForm||!precisionSet||precision!=0) + ca2 = new char[2+p+eSize]; + else + ca2 = new char[1+eSize]; + if (ca1[0]!='0') { + ca2[0] = ca1[0]; + j=1; + } + else { + for (j=1; j<(ePos==-1?ca1.length:ePos); j++) + if (ca1[j]!='0') break; + if ((ePos!=-1 && j<ePos)|| + (ePos==-1 && j<ca1.length)) { + ca2[0] = ca1[j]; + expon -= j; + j++; + } + else { + ca2[0]='0'; + j=2; + } + } + if (alternateForm||!precisionSet||precision!=0) { + ca2[1] = '.'; + i=2; + } + else + i=1; + for (k=0; k<p && j<ca1.length; j++,i++,k++) + ca2[i] = ca1[j]; + for (;i<ca2.length-eSize; i++) + ca2[i] = '0'; + ca2[i++] = eChar; + if (expon<0) ca2[i++]='-'; + else ca2[i++]='+'; + expon = Math.abs(expon); + if (expon>=100) { + switch(expon/100) { + case 1: ca2[i]='1'; break; + case 2: ca2[i]='2'; break; + case 3: ca2[i]='3'; break; + case 4: ca2[i]='4'; break; + case 5: ca2[i]='5'; break; + case 6: ca2[i]='6'; break; + case 7: ca2[i]='7'; break; + case 8: ca2[i]='8'; break; + case 9: ca2[i]='9'; break; + } + i++; + } + switch((expon%100)/10) { + case 0: ca2[i]='0'; break; + case 1: ca2[i]='1'; break; + case 2: ca2[i]='2'; break; + case 3: ca2[i]='3'; break; + case 4: ca2[i]='4'; break; + case 5: ca2[i]='5'; break; + case 6: ca2[i]='6'; break; + case 7: ca2[i]='7'; break; + case 8: ca2[i]='8'; break; + case 9: ca2[i]='9'; break; + } + i++; + switch(expon%10) { + case 0: ca2[i]='0'; break; + case 1: ca2[i]='1'; break; + case 2: ca2[i]='2'; break; + case 3: ca2[i]='3'; break; + case 4: ca2[i]='4'; break; + case 5: ca2[i]='5'; break; + case 6: ca2[i]='6'; break; + case 7: ca2[i]='7'; break; + case 8: ca2[i]='8'; break; + case 9: ca2[i]='9'; break; + } + int nZeros=0; + if (!leftJustify && leadingZeros) { + int xThousands=0; + if (thousands) { + int xlead=0; + if (ca2[0]=='+'||ca2[0]=='-'||ca2[0]==' ') + xlead=1; + int xdp=xlead; + for (; xdp<ca2.length; xdp++) + if (ca2[xdp]=='.') break; + xThousands=(xdp-xlead)/3; + } + if (fieldWidthSet) + nZeros = fieldWidth-ca2.length; + if ((!minusSign&&(leadingSign||leadingSpace))||minusSign) + nZeros--; + nZeros-=xThousands; + if (nZeros<0) nZeros=0; + } + j=0; + if ((!minusSign&&(leadingSign || leadingSpace))||minusSign) { + ca3 = new char[ca2.length+nZeros+1]; + j++; + } + else + ca3 = new char[ca2.length+nZeros]; + if (!minusSign) { + if (leadingSign) ca3[0]='+'; + if (leadingSpace) ca3[0]=' '; + } + else + ca3[0]='-'; + for (k=0; k<nZeros; j++,k++) + ca3[j]='0'; + for (i=0; i<ca2.length && j<ca3.length; i++,j++) + ca3[j]=ca2[i]; + + int lead=0; + if (ca3[0]=='+'||ca3[0]=='-'||ca3[0]==' ') + lead=1; + int dp=lead; + for (; dp<ca3.length; dp++) + if (ca3[dp]=='.') break; + int nThousands=dp/3; + // Localize the decimal point. + if (dp < ca3.length) + ca3[dp] = dfs.getDecimalSeparator(); + char[] ca4 = ca3; + if (thousands && nThousands>0) { + ca4 = new char[ca3.length+nThousands+lead]; + ca4[0]=ca3[0]; + for (i=lead,k=lead; i<dp; i++) { + if (i>0 && (dp-i)%3==0) { + // ca4[k]=','; + ca4[k]=dfs.getGroupingSeparator(); + ca4[k+1]=ca3[i]; + k+=2; + } + else { + ca4[k]=ca3[i]; k++; + } + } + for (; i<ca3.length; i++,k++) + ca4[k]=ca3[i]; + } + return ca4; + } + /** + * Check to see if the digits that are going to + * be truncated because of the precision should + * force a round in the preceding digits. + * @param ca1 the array of digits + * @param icarry the index of the first digit that + * is to be truncated from the print + * @return <code>true</code> if the truncation forces + * a round that will change the print + */ + private boolean checkForCarry(char[] ca1,int icarry) { + boolean carry=false; + if (icarry<ca1.length) { + if (ca1[icarry]=='6'||ca1[icarry]=='7' + ||ca1[icarry]=='8'||ca1[icarry]=='9') carry=true; + else if (ca1[icarry]=='5') { + int ii=icarry+1; + for (;ii<ca1.length; ii++) + if (ca1[ii]!='0') break; + carry=ii<ca1.length; + if (!carry&&icarry>0) { + carry=(ca1[icarry-1]=='1'||ca1[icarry-1]=='3' + ||ca1[icarry-1]=='5'||ca1[icarry-1]=='7' + ||ca1[icarry-1]=='9'); + } + } + } + return carry; + } + /** + * Start the symbolic carry process. The process + * is not quite finished because the symbolic + * carry may change the length of the string and + * change the exponent (in e format). + * @param cLast index of the last digit changed + * by the round + * @param cFirst index of the first digit allowed + * to be changed by this phase of the round + * @return <code>true</code> if the carry forces + * a round that will change the print still + * more + */ + private boolean startSymbolicCarry( + char[] ca,int cLast,int cFirst) { + boolean carry=true; + for (int i=cLast; carry && i>=cFirst; i--) { + carry = false; + switch(ca[i]) { + case '0': ca[i]='1'; break; + case '1': ca[i]='2'; break; + case '2': ca[i]='3'; break; + case '3': ca[i]='4'; break; + case '4': ca[i]='5'; break; + case '5': ca[i]='6'; break; + case '6': ca[i]='7'; break; + case '7': ca[i]='8'; break; + case '8': ca[i]='9'; break; + case '9': ca[i]='0'; carry=true; break; + } + } + return carry; + } + /** + * An intermediate routine on the way to creating + * an e format String. The method decides whether + * the input double value is an infinity, + * not-a-number, or a finite double and formats + * each type of input appropriately. + * @param x the double value to be formatted. + * @param eChar an 'e' or 'E' to use in the + * converted double value. + * @return the converted double value. + */ + private String eFormatString(double x,char eChar) { + boolean noDigits=false; + char[] ca4,ca5; + if (Double.isInfinite(x)) { + if (x==Double.POSITIVE_INFINITY) { + if (leadingSign) ca4 = "+Inf".toCharArray(); + else if (leadingSpace) + ca4 = " Inf".toCharArray(); + else ca4 = "Inf".toCharArray(); + } + else + ca4 = "-Inf".toCharArray(); + noDigits = true; + } + else if (Double.isNaN(x)) { + if (leadingSign) ca4 = "+NaN".toCharArray(); + else if (leadingSpace) + ca4 = " NaN".toCharArray(); + else ca4 = "NaN".toCharArray(); + noDigits = true; + } + else + ca4 = eFormatDigits(x,eChar); + ca5 = applyFloatPadding(ca4,false); + return new String(ca5); + } + /** + * Apply zero or blank, left or right padding. + * @param ca4 array of characters before padding is + * finished + * @param noDigits NaN or signed Inf + * @return a padded array of characters + */ + private char[] applyFloatPadding( + char[] ca4,boolean noDigits) { + char[] ca5 = ca4; + if (fieldWidthSet) { + int i,j,nBlanks; + if (leftJustify) { + nBlanks = fieldWidth-ca4.length; + if (nBlanks > 0) { + ca5 = new char[ca4.length+nBlanks]; + for (i=0; i<ca4.length; i++) + ca5[i] = ca4[i]; + for (j=0; j<nBlanks; j++,i++) + ca5[i] = ' '; + } + } + else if (!leadingZeros || noDigits) { + nBlanks = fieldWidth-ca4.length; + if (nBlanks > 0) { + ca5 = new char[ca4.length+nBlanks]; + for (i=0; i<nBlanks; i++) + ca5[i] = ' '; + for (j=0; j<ca4.length; i++,j++) + ca5[i] = ca4[j]; + } + } + else if (leadingZeros) { + nBlanks = fieldWidth-ca4.length; + if (nBlanks > 0) { + ca5 = new char[ca4.length+nBlanks]; + i=0; j=0; + if (ca4[0]=='-') { ca5[0]='-'; i++; j++; } + for (int k=0; k<nBlanks; i++,k++) + ca5[i] = '0'; + for (; j<ca4.length; i++,j++) + ca5[i] = ca4[j]; + } + } + } + return ca5; + } + /** + * Format method for the f conversion character. + * @param x the double to format. + * @return the formatted String. + */ + private String printFFormat(double x) { + return fFormatString(x); + } + /** + * Format method for the e or E conversion + * character. + * @param x the double to format. + * @return the formatted String. + */ + private String printEFormat(double x) { + if (conversionCharacter=='e') + return eFormatString(x,'e'); + else + return eFormatString(x,'E'); + } + /** + * Format method for the g conversion character. + * + * For g format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear after the radix character. + * Padding is with trailing 0s. + * @param x the double to format. + * @return the formatted String. + */ + private String printGFormat(double x) { + String sx,sy,sz,ret; + int savePrecision=precision; + int i; + char[] ca4,ca5; + boolean noDigits=false; + if (Double.isInfinite(x)) { + if (x==Double.POSITIVE_INFINITY) { + if (leadingSign) ca4 = "+Inf".toCharArray(); + else if (leadingSpace) + ca4 = " Inf".toCharArray(); + else ca4 = "Inf".toCharArray(); + } + else + ca4 = "-Inf".toCharArray(); + noDigits = true; + } + else if (Double.isNaN(x)) { + if (leadingSign) ca4 = "+NaN".toCharArray(); + else if (leadingSpace) + ca4 = " NaN".toCharArray(); + else ca4 = "NaN".toCharArray(); + noDigits = true; + } + else { + if (!precisionSet) precision=defaultDigits; + if (precision==0) precision=1; + int ePos=-1; + if (conversionCharacter=='g') { + sx = eFormatString(x,'e').trim(); + ePos=sx.indexOf('e'); + } + else { + sx = eFormatString(x,'E').trim(); + ePos=sx.indexOf('E'); + } + i=ePos+1; + int expon=0; + if (sx.charAt(i)=='-') { + for (++i; i<sx.length(); i++) + if (sx.charAt(i)!='0') break; + if (i<sx.length()) + expon=-Integer.parseInt(sx.substring(i)); + } + else { + if (sx.charAt(i)=='+') ++i; + for (; i<sx.length(); i++) + if (sx.charAt(i)!='0') break; + if (i<sx.length()) + expon=Integer.parseInt(sx.substring(i)); + } + // Trim trailing zeros. + // If the radix character is not followed by + // a digit, trim it, too. + if (!alternateForm) { + if (expon>=-4 && expon<precision) + sy = fFormatString(x).trim(); + else + sy = sx.substring(0,ePos); + i=sy.length()-1; + for (; i>=0; i--) + if (sy.charAt(i)!='0') break; + if (i>=0 && sy.charAt(i)=='.') i--; + if (i==-1) sz="0"; + else if (!Character.isDigit(sy.charAt(i))) + sz=sy.substring(0,i+1)+"0"; + else sz=sy.substring(0,i+1); + if (expon>=-4 && expon<precision) + ret=sz; + else + ret=sz+sx.substring(ePos); + } + else { + if (expon>=-4 && expon<precision) + ret = fFormatString(x).trim(); + else + ret = sx; + } + // leading space was trimmed off during + // construction + if (leadingSpace) if (x>=0) ret = " "+ret; + ca4 = ret.toCharArray(); + } + // Pad with blanks or zeros. + ca5 = applyFloatPadding(ca4,false); + precision=savePrecision; + return new String(ca5); + } + /** + * Format method for the d conversion specifer and + * short argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the short to format. + * @return the formatted String. + */ + private String printDFormat(short x) { + return printDFormat(Short.toString(x)); + } + /** + * Format method for the d conversion character and + * long argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the long to format. + * @return the formatted String. + */ + private String printDFormat(long x) { + return printDFormat(Long.toString(x)); + } + /** + * Format method for the d conversion character and + * int argument. + * + * For d format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. A '+' character means that the conversion + * will always begin with a sign (+ or -). The + * blank flag character means that a non-negative + * input will be preceded with a blank. If both a + * '+' and a ' ' are specified, the blank flag is + * ignored. The '0' flag character implies that + * padding to the field width will be done with + * zeros instead of blanks. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the int to format. + * @return the formatted String. + */ + private String printDFormat(int x) { + return printDFormat(Integer.toString(x)); + } + /** + * Utility method for formatting using the d + * conversion character. + * @param sx the String to format, the result of + * converting a short, int, or long to a + * String. + * @return the formatted String. + */ + private String printDFormat(String sx) { + int nLeadingZeros=0; + int nBlanks=0,n=0; + int i=0,jFirst=0; + boolean neg = sx.charAt(0)=='-'; + if (sx.equals("0")&&precisionSet&&precision==0) + sx=""; + if (!neg) { + if (precisionSet && sx.length() < precision) + nLeadingZeros = precision-sx.length(); + } + else { + if (precisionSet&&(sx.length()-1)<precision) + nLeadingZeros = precision-sx.length()+1; + } + if (nLeadingZeros<0) nLeadingZeros=0; + if (fieldWidthSet) { + nBlanks = fieldWidth-nLeadingZeros-sx.length(); + if (!neg&&(leadingSign||leadingSpace)) + nBlanks--; + } + if (nBlanks<0) nBlanks=0; + if (leadingSign) n++; + else if (leadingSpace) n++; + n += nBlanks; + n += nLeadingZeros; + n += sx.length(); + char[] ca = new char[n]; + if (leftJustify) { + if (neg) ca[i++] = '-'; + else if (leadingSign) ca[i++] = '+'; + else if (leadingSpace) ca[i++] = ' '; + char[] csx = sx.toCharArray(); + jFirst = neg?1:0; + for (int j=0; j<nLeadingZeros; i++,j++) + ca[i]='0'; + for (int j=jFirst; j<csx.length; j++,i++) + ca[i] = csx[j]; + for (int j=0; j<nBlanks; i++,j++) + ca[i] = ' '; + } + else { + if (!leadingZeros) { + for (i=0; i<nBlanks; i++) + ca[i] = ' '; + if (neg) ca[i++] = '-'; + else if (leadingSign) ca[i++] = '+'; + else if (leadingSpace) ca[i++] = ' '; + } + else { + if (neg) ca[i++] = '-'; + else if (leadingSign) ca[i++] = '+'; + else if (leadingSpace) ca[i++] = ' '; + for (int j=0; j<nBlanks; j++,i++) + ca[i] = '0'; + } + for (int j=0; j<nLeadingZeros; j++,i++) + ca[i] = '0'; + char[] csx = sx.toCharArray(); + jFirst = neg?1:0; + for (int j=jFirst; j<csx.length; j++,i++) + ca[i] = csx[j]; + } + return new String(ca); + } + /** + * Format method for the x conversion character and + * short argument. + * + * For x format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means to lead with + * '0x'. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the short to format. + * @return the formatted String. + */ + private String printXFormat(short x) { + String sx=null; + if (x == Short.MIN_VALUE) + sx = "8000"; + else if (x < 0) { + String t; + if (x==Short.MIN_VALUE) + t = "0"; + else { + t = Integer.toString( + (~(-x-1))^Short.MIN_VALUE,16); + if (t.charAt(0)=='F'||t.charAt(0)=='f') + t = t.substring(16,32); + } + switch (t.length()) { + case 1: + sx = "800"+t; + break; + case 2: + sx = "80"+t; + break; + case 3: + sx = "8"+t; + break; + case 4: + switch (t.charAt(0)) { + case '1': + sx = "9"+t.substring(1,4); + break; + case '2': + sx = "a"+t.substring(1,4); + break; + case '3': + sx = "b"+t.substring(1,4); + break; + case '4': + sx = "c"+t.substring(1,4); + break; + case '5': + sx = "d"+t.substring(1,4); + break; + case '6': + sx = "e"+t.substring(1,4); + break; + case '7': + sx = "f"+t.substring(1,4); + break; + } + break; + } + } + else + sx = Integer.toString((int)x,16); + return printXFormat(sx); + } + /** + * Format method for the x conversion character and + * long argument. + * + * For x format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means to lead with + * '0x'. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the long to format. + * @return the formatted String. + */ + private String printXFormat(long x) { + String sx=null; + if (x == Long.MIN_VALUE) + sx = "8000000000000000"; + else if (x < 0) { + String t = Long.toString( + (~(-x-1))^Long.MIN_VALUE,16); + switch (t.length()) { + case 1: + sx = "800000000000000"+t; + break; + case 2: + sx = "80000000000000"+t; + break; + case 3: + sx = "8000000000000"+t; + break; + case 4: + sx = "800000000000"+t; + break; + case 5: + sx = "80000000000"+t; + break; + case 6: + sx = "8000000000"+t; + break; + case 7: + sx = "800000000"+t; + break; + case 8: + sx = "80000000"+t; + break; + case 9: + sx = "8000000"+t; + break; + case 10: + sx = "800000"+t; + break; + case 11: + sx = "80000"+t; + break; + case 12: + sx = "8000"+t; + break; + case 13: + sx = "800"+t; + break; + case 14: + sx = "80"+t; + break; + case 15: + sx = "8"+t; + break; + case 16: + switch (t.charAt(0)) { + case '1': + sx = "9"+t.substring(1,16); + break; + case '2': + sx = "a"+t.substring(1,16); + break; + case '3': + sx = "b"+t.substring(1,16); + break; + case '4': + sx = "c"+t.substring(1,16); + break; + case '5': + sx = "d"+t.substring(1,16); + break; + case '6': + sx = "e"+t.substring(1,16); + break; + case '7': + sx = "f"+t.substring(1,16); + break; + } + break; + } + } + else + sx = Long.toString(x,16); + return printXFormat(sx); + } + /** + * Format method for the x conversion character and + * int argument. + * + * For x format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means to lead with + * '0x'. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the int to format. + * @return the formatted String. + */ + private String printXFormat(int x) { + String sx=null; + if (x == Integer.MIN_VALUE) + sx = "80000000"; + else if (x < 0) { + String t = Integer.toString( + (~(-x-1))^Integer.MIN_VALUE,16); + switch (t.length()) { + case 1: + sx = "8000000"+t; + break; + case 2: + sx = "800000"+t; + break; + case 3: + sx = "80000"+t; + break; + case 4: + sx = "8000"+t; + break; + case 5: + sx = "800"+t; + break; + case 6: + sx = "80"+t; + break; + case 7: + sx = "8"+t; + break; + case 8: + switch (t.charAt(0)) { + case '1': + sx = "9"+t.substring(1,8); + break; + case '2': + sx = "a"+t.substring(1,8); + break; + case '3': + sx = "b"+t.substring(1,8); + break; + case '4': + sx = "c"+t.substring(1,8); + break; + case '5': + sx = "d"+t.substring(1,8); + break; + case '6': + sx = "e"+t.substring(1,8); + break; + case '7': + sx = "f"+t.substring(1,8); + break; + } + break; + } + } + else + sx = Integer.toString(x,16); + return printXFormat(sx); + } + /** + * Utility method for formatting using the x + * conversion character. + * @param sx the String to format, the result of + * converting a short, int, or long to a + * String. + * @return the formatted String. + */ + private String printXFormat(String sx) { + int nLeadingZeros = 0; + int nBlanks = 0; + if (sx.equals("0")&&precisionSet&&precision==0) + sx=""; + if (precisionSet) + nLeadingZeros = precision-sx.length(); + if (nLeadingZeros<0) nLeadingZeros=0; + if (fieldWidthSet) { + nBlanks = fieldWidth-nLeadingZeros-sx.length(); + if (alternateForm) nBlanks = nBlanks - 2; + } + if (nBlanks<0) nBlanks=0; + int n=0; + if (alternateForm) n+=2; + n += nLeadingZeros; + n += sx.length(); + n += nBlanks; + char[] ca = new char[n]; + int i=0; + if (leftJustify) { + if (alternateForm) { + ca[i++]='0'; ca[i++]='x'; + } + for (int j=0; j<nLeadingZeros; j++,i++) + ca[i]='0'; + char[] csx = sx.toCharArray(); + for (int j=0; j<csx.length; j++,i++) + ca[i] = csx[j]; + for (int j=0; j<nBlanks; j++,i++) + ca[i] = ' '; + } + else { + if (!leadingZeros) + for (int j=0; j<nBlanks; j++,i++) + ca[i] = ' '; + if (alternateForm) { + ca[i++]='0'; ca[i++]='x'; + } + if (leadingZeros) + for (int j=0; j<nBlanks; j++,i++) + ca[i] = '0'; + for (int j=0; j<nLeadingZeros; j++,i++) + ca[i]='0'; + char[] csx = sx.toCharArray(); + for (int j=0; j<csx.length; j++,i++) + ca[i] = csx[j]; + } + String caReturn=new String(ca); + if (conversionCharacter=='X') + caReturn = caReturn.toUpperCase(); + return caReturn; + } + /** + * Format method for the o conversion character and + * short argument. + * + * For o format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means that the + * output begins with a leading 0 and the precision + * is increased by 1. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the short to format. + * @return the formatted String. + */ + private String printOFormat(short x) { + String sx=null; + if (x == Short.MIN_VALUE) + sx = "100000"; + else if (x < 0) { + String t = Integer.toString( + (~(-x-1))^Short.MIN_VALUE,8); + switch (t.length()) { + case 1: + sx = "10000"+t; + break; + case 2: + sx = "1000"+t; + break; + case 3: + sx = "100"+t; + break; + case 4: + sx = "10"+t; + break; + case 5: + sx = "1"+t; + break; + } + } + else + sx = Integer.toString((int)x,8); + return printOFormat(sx); + } + /** + * Format method for the o conversion character and + * long argument. + * + * For o format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means that the + * output begins with a leading 0 and the precision + * is increased by 1. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the long to format. + * @return the formatted String. + */ + private String printOFormat(long x) { + String sx=null; + if (x == Long.MIN_VALUE) + sx = "1000000000000000000000"; + else if (x < 0) { + String t = Long.toString( + (~(-x-1))^Long.MIN_VALUE,8); + switch (t.length()) { + case 1: + sx = "100000000000000000000"+t; + break; + case 2: + sx = "10000000000000000000"+t; + break; + case 3: + sx = "1000000000000000000"+t; + break; + case 4: + sx = "100000000000000000"+t; + break; + case 5: + sx = "10000000000000000"+t; + break; + case 6: + sx = "1000000000000000"+t; + break; + case 7: + sx = "100000000000000"+t; + break; + case 8: + sx = "10000000000000"+t; + break; + case 9: + sx = "1000000000000"+t; + break; + case 10: + sx = "100000000000"+t; + break; + case 11: + sx = "10000000000"+t; + break; + case 12: + sx = "1000000000"+t; + break; + case 13: + sx = "100000000"+t; + break; + case 14: + sx = "10000000"+t; + break; + case 15: + sx = "1000000"+t; + break; + case 16: + sx = "100000"+t; + break; + case 17: + sx = "10000"+t; + break; + case 18: + sx = "1000"+t; + break; + case 19: + sx = "100"+t; + break; + case 20: + sx = "10"+t; + break; + case 21: + sx = "1"+t; + break; + } + } + else + sx = Long.toString(x,8); + return printOFormat(sx); + } + /** + * Format method for the o conversion character and + * int argument. + * + * For o format, the flag character '-', means that + * the output should be left justified within the + * field. The default is to pad with blanks on the + * left. The '#' flag character means that the + * output begins with a leading 0 and the precision + * is increased by 1. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is to + * add no padding. Padding is with blanks by + * default. + * + * The precision, if set, is the minimum number of + * digits to appear. Padding is with leading 0s. + * @param x the int to format. + * @return the formatted String. + */ + private String printOFormat(int x) { + String sx=null; + if (x == Integer.MIN_VALUE) + sx = "20000000000"; + else if (x < 0) { + String t = Integer.toString( + (~(-x-1))^Integer.MIN_VALUE,8); + switch (t.length()) { + case 1: + sx = "2000000000"+t; + break; + case 2: + sx = "200000000"+t; + break; + case 3: + sx = "20000000"+t; + break; + case 4: + sx = "2000000"+t; + break; + case 5: + sx = "200000"+t; + break; + case 6: + sx = "20000"+t; + break; + case 7: + sx = "2000"+t; + break; + case 8: + sx = "200"+t; + break; + case 9: + sx = "20"+t; + break; + case 10: + sx = "2"+t; + break; + case 11: + sx = "3"+t.substring(1); + break; + } + } + else + sx = Integer.toString(x,8); + return printOFormat(sx); + } + /** + * Utility method for formatting using the o + * conversion character. + * @param sx the String to format, the result of + * converting a short, int, or long to a + * String. + * @return the formatted String. + */ + private String printOFormat(String sx) { + int nLeadingZeros = 0; + int nBlanks = 0; + if (sx.equals("0")&&precisionSet&&precision==0) + sx=""; + if (precisionSet) + nLeadingZeros = precision-sx.length(); + if (alternateForm) nLeadingZeros++; + if (nLeadingZeros<0) nLeadingZeros=0; + if (fieldWidthSet) + nBlanks = fieldWidth-nLeadingZeros-sx.length(); + if (nBlanks<0) nBlanks=0; + int n=nLeadingZeros+sx.length()+nBlanks; + char[] ca = new char[n]; + int i; + if (leftJustify) { + for (i=0; i<nLeadingZeros; i++) ca[i]='0'; + char[] csx = sx.toCharArray(); + for (int j=0; j<csx.length; j++,i++) + ca[i] = csx[j]; + for (int j=0; j<nBlanks; j++,i++) ca[i] = ' '; + } + else { + if (leadingZeros) + for (i=0; i<nBlanks; i++) ca[i]='0'; + else + for (i=0; i<nBlanks; i++) ca[i]=' '; + for (int j=0; j<nLeadingZeros; j++,i++) + ca[i]='0'; + char[] csx = sx.toCharArray(); + for (int j=0; j<csx.length; j++,i++) + ca[i] = csx[j]; + } + return new String(ca); + } + /** + * Format method for the c conversion character and + * char argument. + * + * The only flag character that affects c format is + * the '-', meaning that the output should be left + * justified within the field. The default is to + * pad with blanks on the left. + * + * The field width is treated as the minimum number + * of characters to be printed. Padding is with + * blanks by default. The default width is 1. + * + * The precision, if set, is ignored. + * @param x the char to format. + * @return the formatted String. + */ + private String printCFormat(char x) { + int nPrint = 1; + int width = fieldWidth; + if (!fieldWidthSet) width = nPrint; + char[] ca = new char[width]; + int i=0; + if (leftJustify) { + ca[0] = x; + for (i=1; i<=width-nPrint; i++) ca[i]=' '; + } + else { + for (i=0; i<width-nPrint; i++) ca[i]=' '; + ca[i] = x; + } + return new String(ca); + } + /** + * Format method for the s conversion character and + * String argument. + * + * The only flag character that affects s format is + * the '-', meaning that the output should be left + * justified within the field. The default is to + * pad with blanks on the left. + * + * The field width is treated as the minimum number + * of characters to be printed. The default is the + * smaller of the number of characters in the the + * input and the precision. Padding is with blanks + * by default. + * + * The precision, if set, specifies the maximum + * number of characters to be printed from the + * string. A null digit string is treated + * as a 0. The default is not to set a maximum + * number of characters to be printed. + * @param x the String to format. + * @return the formatted String. + */ + private String printSFormat(String x) { + int nPrint = x.length(); + int width = fieldWidth; + if (precisionSet && nPrint>precision) + nPrint=precision; + if (!fieldWidthSet) width = nPrint; + int n=0; + if (width>nPrint) n+=width-nPrint; + if (nPrint>=x.length()) n+= x.length(); + else n+= nPrint; + char[] ca = new char[n]; + int i=0; + if (leftJustify) { + if (nPrint>=x.length()) { + char[] csx = x.toCharArray(); + for (i=0; i<x.length(); i++) ca[i]=csx[i]; + } + else { + char[] csx = + x.substring(0,nPrint).toCharArray(); + for (i=0; i<nPrint; i++) ca[i]=csx[i]; + } + for (int j=0; j<width-nPrint; j++,i++) + ca[i]=' '; + } + else { + for (i=0; i<width-nPrint; i++) ca[i]=' '; + if (nPrint>=x.length()) { + char[] csx = x.toCharArray(); + for (int j=0; j<x.length(); i++,j++) + ca[i]=csx[j]; + } + else { + char[] csx = + x.substring(0,nPrint).toCharArray(); + for (int j=0; j<nPrint; i++,j++) + ca[i]=csx[j]; + } + } + return new String(ca); + } + /** + * Check for a conversion character. If it is + * there, store it. + * @param x the String to format. + * @return <code>true</code> if the conversion + * character is there, and + * <code>false</code> otherwise. + */ + private boolean setConversionCharacter() { + /* idfgGoxXeEcs */ + boolean ret = false; + conversionCharacter='\0'; + if (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (c=='i'||c=='d'||c=='f'||c=='g'||c=='G' + || c=='o' || c=='x' || c=='X' || c=='e' + || c=='E' || c=='c' || c=='s' || c=='%') { + conversionCharacter = c; + pos++; + ret = true; + } + } + return ret; + } + /** + * Check for an h, l, or L in a format. An L is + * used to control the minimum number of digits + * in an exponent when using floating point + * formats. An l or h is used to control + * conversion of the input to a long or short, + * respectively, before formatting. If any of + * these is present, store them. + */ + private void setOptionalHL() { + optionalh=false; + optionall=false; + optionalL=false; + if (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (c=='h') { optionalh=true; pos++; } + else if (c=='l') { optionall=true; pos++; } + else if (c=='L') { optionalL=true; pos++; } + } + } + /** + * Set the precision. + */ + private void setPrecision() { + int firstPos = pos; + precisionSet = false; + if (pos<fmt.length()&&fmt.charAt(pos)=='.') { + pos++; + if ((pos < fmt.length()) + && (fmt.charAt(pos)=='*')) { + pos++; + if (!setPrecisionArgPosition()) { + variablePrecision = true; + precisionSet = true; + } + return; + } + else { + while (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (Character.isDigit(c)) pos++; + else break; + } + if (pos > firstPos+1) { + String sz = fmt.substring(firstPos+1,pos); + precision = Integer.parseInt(sz); + precisionSet = true; + } + } + } + } + /** + * Set the field width. + */ + private void setFieldWidth() { + int firstPos = pos; + fieldWidth = 0; + fieldWidthSet = false; + if ((pos < fmt.length()) + && (fmt.charAt(pos)=='*')) { + pos++; + if (!setFieldWidthArgPosition()) { + variableFieldWidth = true; + fieldWidthSet = true; + } + } + else { + while (pos < fmt.length()) { + char c = fmt.charAt(pos); + if (Character.isDigit(c)) pos++; + else break; + } + if (firstPos<pos && firstPos < fmt.length()) { + String sz = fmt.substring(firstPos,pos); + fieldWidth = Integer.parseInt(sz); + fieldWidthSet = true; + } + } + } + /** + * Store the digits <code>n</code> in %n$ forms. + */ + private void setArgPosition() { + int xPos; + for (xPos=pos; xPos<fmt.length(); xPos++) { + if (!Character.isDigit(fmt.charAt(xPos))) + break; + } + if (xPos>pos && xPos<fmt.length()) { + if (fmt.charAt(xPos)=='$') { + positionalSpecification = true; + argumentPosition= + Integer.parseInt(fmt.substring(pos,xPos)); + pos=xPos+1; + } + } + } + /** + * Store the digits <code>n</code> in *n$ forms. + */ + private boolean setFieldWidthArgPosition() { + boolean ret=false; + int xPos; + for (xPos=pos; xPos<fmt.length(); xPos++) { + if (!Character.isDigit(fmt.charAt(xPos))) + break; + } + if (xPos>pos && xPos<fmt.length()) { + if (fmt.charAt(xPos)=='$') { + positionalFieldWidth = true; + argumentPositionForFieldWidth= + Integer.parseInt(fmt.substring(pos,xPos)); + pos=xPos+1; + ret=true; + } + } + return ret; + } + /** + * Store the digits <code>n</code> in *n$ forms. + */ + private boolean setPrecisionArgPosition() { + boolean ret=false; + int xPos; + for (xPos=pos; xPos<fmt.length(); xPos++) { + if (!Character.isDigit(fmt.charAt(xPos))) + break; + } + if (xPos>pos && xPos<fmt.length()) { + if (fmt.charAt(xPos)=='$') { + positionalPrecision = true; + argumentPositionForPrecision= + Integer.parseInt(fmt.substring(pos,xPos)); + pos=xPos+1; + ret=true; + } + } + return ret; + } + boolean isPositionalSpecification() { + return positionalSpecification; + } + int getArgumentPosition() { return argumentPosition; } + boolean isPositionalFieldWidth() { + return positionalFieldWidth; + } + int getArgumentPositionForFieldWidth() { + return argumentPositionForFieldWidth; + } + boolean isPositionalPrecision() { + return positionalPrecision; + } + int getArgumentPositionForPrecision() { + return argumentPositionForPrecision; + } + /** + * Set flag characters, one of '-+#0 or a space. + */ + private void setFlagCharacters() { + /* '-+ #0 */ + thousands = false; + leftJustify = false; + leadingSign = false; + leadingSpace = false; + alternateForm = false; + leadingZeros = false; + for ( ; pos < fmt.length(); pos++) { + char c = fmt.charAt(pos); + if (c == '\'') thousands = true; + else if (c == '-') { + leftJustify = true; + leadingZeros = false; + } + else if (c == '+') { + leadingSign = true; + leadingSpace = false; + } + else if (c == ' ') { + if (!leadingSign) leadingSpace = true; + } + else if (c == '#') alternateForm = true; + else if (c == '0') { + if (!leftJustify) leadingZeros = true; + } + else break; + } + } + /** + * The integer portion of the result of a decimal + * conversion (i, d, u, f, g, or G) will be + * formatted with thousands' grouping characters. + * For other conversions the flag is ignored. + */ + private boolean thousands = false; + /** + * The result of the conversion will be + * left-justified within the field. + */ + private boolean leftJustify = false; + /** + * The result of a signed conversion will always + * begin with a sign (+ or -). + */ + private boolean leadingSign = false; + /** + * Flag indicating that left padding with spaces is + * specified. + */ + private boolean leadingSpace = false; + /** + * For an o conversion, increase the precision to + * force the first digit of the result to be a + * zero. For x (or X) conversions, a non-zero + * result will have 0x (or 0X) prepended to it. + * For e, E, f, g, or G conversions, the result + * will always contain a radix character, even if + * no digits follow the point. For g and G + * conversions, trailing zeros will not be removed + * from the result. + */ + private boolean alternateForm = false; + /** + * Flag indicating that left padding with zeroes is + * specified. + */ + private boolean leadingZeros = false; + /** + * Flag indicating that the field width is *. + */ + private boolean variableFieldWidth = false; + /** + * If the converted value has fewer bytes than the + * field width, it will be padded with spaces or + * zeroes. + */ + private int fieldWidth = 0; + /** + * Flag indicating whether or not the field width + * has been set. + */ + private boolean fieldWidthSet = false; + /** + * The minimum number of digits to appear for the + * d, i, o, u, x, or X conversions. The number of + * digits to appear after the radix character for + * the e, E, and f conversions. The maximum number + * of significant digits for the g and G + * conversions. The maximum number of bytes to be + * printed from a string in s and S conversions. + */ + private int precision = 0; + /** Default precision. */ + private final static int defaultDigits=6; + /** + * Flag indicating that the precision is *. + */ + private boolean variablePrecision = false; + /** + * Flag indicating whether or not the precision has + * been set. + */ + private boolean precisionSet = false; + /* + */ + private boolean positionalSpecification=false; + private int argumentPosition=0; + private boolean positionalFieldWidth=false; + private int argumentPositionForFieldWidth=0; + private boolean positionalPrecision=false; + private int argumentPositionForPrecision=0; + /** + * Flag specifying that a following d, i, o, u, x, + * or X conversion character applies to a type + * short int. + */ + private boolean optionalh = false; + /** + * Flag specifying that a following d, i, o, u, x, + * or X conversion character applies to a type lont + * int argument. + */ + private boolean optionall = false; + /** + * Flag specifying that a following e, E, f, g, or + * G conversion character applies to a type double + * argument. This is a noop in Java. + */ + private boolean optionalL = false; + /** Control string type. */ + private char conversionCharacter = '\0'; + /** + * Position within the control string. Used by + * the constructor. + */ + private int pos = 0; + /** Literal or control format string. */ + private String fmt; + } + /** Vector of control strings and format literals. */ + private Vector vFmt = new Vector(); + /** Character position. Used by the constructor. */ + private int cPos=0; + /** Character position. Used by the constructor. */ + private DecimalFormatSymbols dfs=null; +} diff --git a/src/main/java/fr/inria/optimization/cmaes/examples/CMAExample1.java b/src/main/java/fr/inria/optimization/cmaes/examples/CMAExample1.java new file mode 100644 index 0000000000000000000000000000000000000000..732f10428660d18faec5cfcbe6d55155a684230c --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/examples/CMAExample1.java @@ -0,0 +1,141 @@ +package fr.inria.optimization.cmaes.examples; +import fr.inria.optimization.cmaes.CMAEvolutionStrategy; +import fr.inria.optimization.cmaes.fitness.IObjectiveFunction; + +/** The very well-known Rosenbrock objective function to be minimized. + */ +class Rosenbrock implements IObjectiveFunction { // meaning implements methods valueOf and isFeasible + public double valueOf (double[] x) { + double res = 0; + for (int i = 0; i < x.length-1; ++i) + res += 100 * (x[i]*x[i] - x[i+1]) * (x[i]*x[i] - x[i+1]) + + (x[i] - 1.) * (x[i] - 1.); + return res; + } + public boolean isFeasible(double[] x) {return true; } // entire R^n is feasible +} + +/** A very short example program how to use the class CMAEvolutionStrategy. The code is given below, see also the code snippet in the documentation of class {@link CMAEvolutionStrategy}. + * For implementation of restarts see {@link CMAExample2}. +<pre> +public class CMAExample1 { + public static void main(String[] args) { + IObjectiveFunction fitfun = new Rosenbrock(); + + // new a CMA-ES and set some initial values + CMAEvolutionStrategy cma = new CMAEvolutionStrategy(); + cma.readProperties(); // read options, see file CMAEvolutionStrategy.properties + cma.setDimension(22); // overwrite some loaded properties + cma.setInitialX(0.5); // in each dimension, also setTypicalX can be used + cma.setInitialStandardDeviation(0.2); // also a mandatory setting + cma.options.stopFitness = 1e-9; // optional setting + + // initialize cma and get fitness array to fill in later + double[] fitness = cma.init(); // new double[cma.parameters.getPopulationSize()]; + + // initial output to files + cma.writeToDefaultFilesHeaders(0); // 0 == overwrites old files + + // iteration loop + while(cma.stopConditions.getNumber() == 0) { + + // core iteration step + double[][] pop = cma.samplePopulation(); // get a new population of solutions + for(int i = 0; i < pop.length; ++i) { // for each candidate solution i + while (!fitfun.isFeasible(pop[i])) // test whether solution is feasible, + pop[i] = cma.resampleSingle(i); // re-sample solution until it is feasible + fitness[i] = fitfun.valueOf(pop[i]); // compute fitness value, where fitfun + } // is the function to be minimized + cma.updateDistribution(fitness); // pass fitness array to update search distribution + + // output to console and files + cma.writeToDefaultFiles(); + int outmod = 150; + if (cma.getCountIter() % (15*outmod) == 1) + cma.printlnAnnotation(); // might write file as well + if (cma.getCountIter() % outmod == 1) + cma.println(); + } + // evaluate mean value as it is the best estimator for the optimum + cma.setFitnessOfMeanX(fitfun.valueOf(cma.getMeanX())); // updates the best ever solution + + // final output + cma.writeToDefaultFiles(1); + cma.println(); + cma.println("Terminated due to"); + for (String s : cma.stopConditions.getMessages()) + cma.println(" " + s); + cma.println("best function value " + cma.getBestFunctionValue() + + " at evaluation " + cma.getBestEvaluationNumber()); + + // we might return cma.getBestSolution() or cma.getBestX() + + } // main +} // class +</pre> + + * + * @see CMAEvolutionStrategy + * + * @author Nikolaus Hansen, released into public domain. + */ +public class CMAExample1 { + public static void main(String[] args) { + IObjectiveFunction fitfun = new Rosenbrock(); + + // new a CMA-ES and set some initial values + CMAEvolutionStrategy cma = new CMAEvolutionStrategy(); + cma.readProperties(); // read options, see file CMAEvolutionStrategy.properties + cma.setDimension(10); // overwrite some loaded properties + cma.setInitialX(0.05); // in each dimension, also setTypicalX can be used + cma.setInitialStandardDeviation(0.2); // also a mandatory setting + cma.options.stopFitness = 1e-14; // optional setting + + // initialize cma and get fitness array to fill in later + double[] fitness = cma.init(); // new double[cma.parameters.getPopulationSize()]; + + // initial output to files + cma.writeToDefaultFilesHeaders(0); // 0 == overwrites old files + + // iteration loop + while(cma.stopConditions.getNumber() == 0) { + + // --- core iteration step --- + double[][] pop = cma.samplePopulation(); // get a new population of solutions + for(int i = 0; i < pop.length; ++i) { // for each candidate solution i + // a simple way to handle constraints that define a convex feasible domain + // (like box constraints, i.e. variable boundaries) via "blind re-sampling" + // assumes that the feasible domain is convex, the optimum is + while (!fitfun.isFeasible(pop[i])) // not located on (or very close to) the domain boundary, + pop[i] = cma.resampleSingle(i); // initialX is feasible and initialStandardDeviations are + // sufficiently small to prevent quasi-infinite looping here + // compute fitness/objective value + fitness[i] = fitfun.valueOf(pop[i]); // fitfun.valueOf() is to be minimized + } + cma.updateDistribution(fitness); // pass fitness array to update search distribution + // --- end core iteration step --- + + // output to files and console + cma.writeToDefaultFiles(); + int outmod = 150; + if (cma.getCountIter() % (15*outmod) == 1) + cma.printlnAnnotation(); // might write file as well + if (cma.getCountIter() % outmod == 1) + cma.println(); + } + // evaluate mean value as it is the best estimator for the optimum + cma.setFitnessOfMeanX(fitfun.valueOf(cma.getMeanX())); // updates the best ever solution + + // final output + cma.writeToDefaultFiles(1); + cma.println(); + cma.println("Terminated due to"); + for (String s : cma.stopConditions.getMessages()) + cma.println(" " + s); + cma.println("best function value " + cma.getBestFunctionValue() + + " at evaluation " + cma.getBestEvaluationNumber()); + + // we might return cma.getBestSolution() or cma.getBestX() + + } // main +} // class diff --git a/src/main/java/fr/inria/optimization/cmaes/examples/CMAExample2.java b/src/main/java/fr/inria/optimization/cmaes/examples/CMAExample2.java new file mode 100644 index 0000000000000000000000000000000000000000..03d563adb13d79332143fb3bb726aa10c07f2b79 --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/examples/CMAExample2.java @@ -0,0 +1,155 @@ +package fr.inria.optimization.cmaes.examples; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import fr.inria.optimization.cmaes.CMAEvolutionStrategy; +import fr.inria.optimization.cmaes.CMAOptions; +import fr.inria.optimization.cmaes.CMASolution; +import fr.inria.optimization.cmaes.fitness.FunctionCollector; +import fr.inria.optimization.cmaes.fitness.IObjectiveFunction; + +/** + * Example of how to use the class CMAEvolutionStrategy including restarts with increasing + * population size (IPOP). Copy and modify the code to your convenience. Final termination criteria + * are stopFitness and stopMaxFunEvals (see class {@link CMAOptions}). The remaining + * termination criteria invoke a restart with increased population size (see incPopSizeFactor in file + * CMAEvolutionStrategy.properties). + * + * @see CMAEvolutionStrategy + * + * @author Nikolaus Hansen, released into public domain. + */ +public class CMAExample2 { + + public static void main(String[] args) { + int irun, nbRuns=1; // restarts, re-read from properties file below + double [] fitness; + CMASolution bestSolution = null; // initialization to allow compilation + long counteval = 0; // variables used for restart + int lambda = 0; + + for (irun = 0; irun < nbRuns; ++irun) { // might also terminate before + + CMAEvolutionStrategy cma = new CMAEvolutionStrategy(); + + // read properties file and obtain some values for "private" use + cma.readProperties(); // reads from file CMAEvolutionStrategy.properties + //cma.setInitialX(-20, 80); + //cma.setInitialStandardDeviation(0.3 * 100); + + // set up fitness function + double nbFunc = cma.options.getFirstToken(cma.getProperties().getProperty("functionNumber"), 10); + int rotate = cma.options.getFirstToken(cma.getProperties().getProperty("functionRotate"), 0); + double axisratio = cma.options.getFirstToken(cma.getProperties().getProperty("functionAxisRatio"), 0.); + IObjectiveFunction fitfun = new FunctionCollector(nbFunc, rotate, axisratio); + + // set up restarts + nbRuns = 1+cma.options.getFirstToken(cma.getProperties().getProperty("numberOfRestarts"), 1); + double incPopSizeFactor = cma.options.getFirstToken(cma.getProperties().getProperty("incPopSizeFactor"), 1.); + + // initialize + if (irun == 0) { + fitness = cma.init(); // finalize setting of population size lambda, get fitness array + lambda = cma.parameters.getPopulationSize(); // retain lambda for restart + cma.writeToDefaultFilesHeaders(0); // overwrite output files + } + else { + cma.parameters.setPopulationSize((int)Math.ceil(lambda * Math.pow(incPopSizeFactor, irun))); + cma.setCountEval(counteval); // somehow a hack + fitness = cma.init(); // provides array to assign fitness values + } + + // set additional termination criterion + if (nbRuns > 1) + cma.options.stopMaxIter = (long) (100 + 200*Math.pow(cma.getDimension(),2)*Math.sqrt(cma.parameters.getLambda())); + + // iteration loop + double lastTime = 0, alastTime = 0; // for smarter console output + while(cma.stopConditions.isFalse()) { + + // --- core iteration step --- + double[][] pop = cma.samplePopulation(); // get a new population of solutions + for(int i = 0; i < pop.length; ++i) { // for each candidate solution i + // a simple way to handle constraints that define a convex feasible domain + // (like box constraints, i.e. variable boundaries) via "blind re-sampling" + // assumes that the feasible domain is convex, the optimum is + while (!fitfun.isFeasible(pop[i])) // not located on (or very close to) the domain boundary, + pop[i] = cma.resampleSingle(i); // initialX is feasible and initialStandardDeviations are + // sufficiently small to prevent quasi-infinite looping here + // compute fitness/objective value + fitness[i] = fitfun.valueOf(pop[i]); // fitfun.valueOf() is to be minimized + } + cma.updateDistribution(fitness); // pass fitness array to update search distribution + // --- end core iteration step --- + + // stopping conditions can be changed in file CMAEvolutionStrategy.properties + cma.readProperties(); + + // the remainder is output + cma.writeToDefaultFiles(); + + // screen output + boolean printsomething = true; // for a convenient switch to false + if (printsomething && System.currentTimeMillis() - alastTime > 20e3) { + cma.printlnAnnotation(); + alastTime = System.currentTimeMillis(); + } + if (printsomething && (cma.stopConditions.isTrue() || cma.getCountIter() < 4 + || (cma.getCountIter() > 0 && (Math.log10(cma.getCountIter()) % 1) < 1e-11) + || System.currentTimeMillis() - lastTime > 2.5e3)) { // wait 2.5 seconds + cma.println(); + lastTime = System.currentTimeMillis(); + } + } // iteration loop + + // evaluate mean value as it is the best estimator for the optimum + cma.setFitnessOfMeanX(fitfun.valueOf(cma.getMeanX())); // updates the best ever solution + + // retain best solution ever found + if (irun == 0) + bestSolution = cma.getBestSolution(); + else if (cma.getBestSolution().getFitness() < bestSolution.getFitness()) + bestSolution = cma.getBestSolution(); + + // final output for the run + cma.writeToDefaultFiles(1); // 1 == make sure to write final result + cma.println("Terminated (run " + (irun+1) + ") due to"); + for (String s : cma.stopConditions.getMessages()) + cma.println(" " + s); + cma.println(" best function value " + cma.getBestFunctionValue() + + " at evaluation " + cma.getBestEvaluationNumber()); + + // quit restart loop if MaxFunEvals or target Fitness are reached + boolean quit = false; + for (String s : cma.stopConditions.getMessages()) + if (s.startsWith("MaxFunEvals") || + s.startsWith("Fitness")) + quit = true; + if (quit) + break; + + counteval = cma.getCountEval(); + + if (irun < nbRuns-1) // after Manual stop give time out to change stopping condition + for (String s : cma.stopConditions.getMessages()) + if (s.startsWith("Manual")) { + System.out.println("incomment 'stop now' and press return to start next run"); + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + try { in.readLine(); } + catch(IOException e) { System.out.println("input not readable"); } + } + + } // for irun < nbRuns + + // screen output + if (irun > 1) { + System.out.println(" " + (irun) + " runs conducted," + + " best function value " + bestSolution.getFitness() + + " at evaluation " + bestSolution.getEvaluationNumber()); + } + + } // main +} // class + + diff --git a/src/main/java/fr/inria/optimization/cmaes/fitness/AbstractObjectiveFunction.java b/src/main/java/fr/inria/optimization/cmaes/fitness/AbstractObjectiveFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..84a733b850f4a27ade3d44a248ffdba94ed5e166 --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/fitness/AbstractObjectiveFunction.java @@ -0,0 +1,20 @@ +package fr.inria.optimization.cmaes.fitness; + + +/** extending from this abstract class implements a generic isFeasible method and adds the + * IObjectiveFunctionParallel interface to a class that implements + * the interface IObjectiveFunction */ +public abstract class AbstractObjectiveFunction implements +IObjectiveFunction, +IObjectiveFunctionParallel { + abstract public double valueOf(double[] x); + public double [] valuesOf(double[][] pop) { + double [] res = new double[pop.length]; + for (int i = 0; i < pop.length; ++i) + res[i] = valueOf(pop[i]); + return res; + } + public boolean isFeasible(double[] x) { + return true; + } +} diff --git a/src/main/java/fr/inria/optimization/cmaes/fitness/FunctionCollector.java b/src/main/java/fr/inria/optimization/cmaes/fitness/FunctionCollector.java new file mode 100644 index 0000000000000000000000000000000000000000..535ba0a212ef7c1109011720650c772a6c9c7a00 --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/fitness/FunctionCollector.java @@ -0,0 +1,348 @@ +package fr.inria.optimization.cmaes.fitness; +import java.util.Random; + +/** one can access the desired fitness function by giving its number + * in the constructor method. Refer to the source code for the + * numbers. This class is a stub (and hack) so far. + * + */ +public class FunctionCollector extends AbstractObjectiveFunction { + + public FunctionCollector (double function_number, + int flgRotate, + double axisratio) { + + actFun = (int) (function_number); + rotate = flgRotate; + scaling = axisratio == 0 ? 1. : axisratio; + + if (actFun > maxFuncNumber) + actFun = 1; /* sphere */ + + // assign all functions by number here + funs[0] = new RandFun(); + funs[10] = new Sphere(); + + // convex-quadratic + funs[30] = new Cigar(axisratio == 0 ? 1e3 : scaling); + funs[40] = new Tablet(axisratio == 0 ? 1e3 : scaling); + funs[50] = new Elli(axisratio == 0 ? 1e3 : scaling); + funs[60] = new CigTab(axisratio == 0 ? 1e4 : scaling); + funs[70] = new TwoAxes(axisratio == 0 ? 1e3 : scaling); + + // uni-modal, well, essentially + funs[80] = new Rosen(); + funs[90] = new DiffPow(); + funs[91] = new ssDiffPow(); + + // multi-modal + funs[150] = new Rastrigin(scaling, 10); + funs[160] = new Ackley(scaling); + +// funs[999] = new Experimental(); +// funs[] = new (); +// funs[] = new (); + + } + final int maxFuncNumber = 999; + IObjectiveFunction[] funs = new IObjectiveFunction[maxFuncNumber+1]; + int actFun = 0; + int rotate = 0; + double scaling = 1; + Basis B = new Basis(); + + /** implements the fitness function evaluation according to interface {@link IObjectiveFunction} + * + */ + @Override + public double valueOf(double[] x) { + x = x.clone(); // regard input as imutable, not really Java philosophy + if (rotate > 0) // rotate + x = B.Rotate(x); + if (scaling != 1) { // scale + for (int i = 0; i < x.length; ++i) + x[i] = Math.pow(10, i/(x.length -1.)) * x[i]; + } + return funs[actFun] == null ? funs[0].valueOf(x) : funs[actFun].valueOf(x); + } + public boolean isFeasible(double x[]) { // unfortunate code duplication + //int i; + //for (i = 0; i < x.length; ++i) + // if (x[i] < 0.01) + // return false; + //return true; + return funs[actFun].isFeasible(x); + } +} + +/** provides rotation of a search point, basis is chosen with constant seed. + * + */ +class RandFun extends AbstractObjectiveFunction { + java.util.Random rand = new java.util.Random(0); + @Override + public double valueOf (double[] x) { + double res = rand.nextDouble(); + return res; + } +} +class Sphere extends AbstractObjectiveFunction { + @Override + public double valueOf (double[] x) { + double res = 0; + for (int i = 0; i < x.length; ++i) + res += x[i] * x[i]; + return res; + } + public boolean isFeasible(double[] x) { + //int i; + //for (i = 0; i < x.length; ++i) + // if (x[i] < 0.01) + // return false; + return true; + } +} + +class Cigar extends AbstractObjectiveFunction { + Cigar() { + this(1e3); + } + Cigar(double axisratio) { + factor = axisratio * axisratio; + } + public double factor = 1e6; + @Override + public double valueOf (double[] x) { + double res = x[0] * x[0]; + for (int i = 1; i < x.length; ++i) + res += factor * x[i] * x[i]; + return res; + } +} +class Tablet extends AbstractObjectiveFunction { + Tablet() { + this(1e3); + } + Tablet(double axisratio) { + factor = axisratio * axisratio; + } + public double factor = 1e6; + @Override + public double valueOf (double[] x) { + double res = factor * x[0] * x[0]; + for (int i = 1; i < x.length; ++i) + res += x[i] * x[i]; + return res; + } +} +class CigTab extends AbstractObjectiveFunction { + CigTab() { + this(1e4); + } + CigTab(double axisratio) { + factor = axisratio; + } + public double factor = 1e6; + @Override + public double valueOf (double[] x) { + int end = x.length-1; + double res = x[0] * x[0] / factor + factor * x[end] * x[end]; + for (int i = 1; i < end; ++i) + res += x[i] * x[i]; + return res; + } +} +class TwoAxes extends AbstractObjectiveFunction { + public double factor = 1e6; + TwoAxes() { + } + TwoAxes(double axisratio) { + factor = axisratio * axisratio; + } + @Override + public double valueOf (double[] x) { + double res = 0; + for (int i = 0; i < x.length; ++i) + res += (i < x.length/2 ? factor : 1) * x[i] * x[i]; + return res; + } +} +class ElliRotated extends AbstractObjectiveFunction { + ElliRotated() { + this(1e3); + } + ElliRotated(double axisratio) { + factor = axisratio * axisratio; + } + public Basis B = new Basis(); + public double factor = 1e6; + @Override + public double valueOf (double[] x) { + x = B.Rotate(x); + double res = 0; + for (int i = 0; i < x.length; ++i) + res += Math.pow(factor,i/(x.length-1.)) * x[i] * x[i]; + return res; + } +} +/** dimensionality must be larger than one */ +class Elli extends AbstractObjectiveFunction { + Elli() { + this(1e3); + } + Elli(double axisratio) { + factor = axisratio * axisratio; + } + public double factor = 1e6; + @Override + public double valueOf (double[] x) { + double res = 0; + for (int i = 0; i < x.length; ++i) + res += Math.pow(factor,i/(x.length-1.)) * x[i] * x[i]; + return res; + } +// public boolean isFeasible(double x[]) { +// int i; +// for (i = 0; i < x.length; ++i) { +// if (x[i] < -0.20 || x[i] > 80) +// return false; +// } +// return true; +// } + +}/** dimensionality must be larger than one */ + +class DiffPow extends AbstractObjectiveFunction { + @Override + public double valueOf (double[] x) { + double res = 0; + for (int i = 0; i < x.length; ++i) + res += Math.pow(Math.abs(x[i]),2.+10*(double)i/(x.length-1.)); + return res; + } + +}class ssDiffPow extends AbstractObjectiveFunction { + @Override + public double valueOf (double[] x) { + return Math.pow(new DiffPow().valueOf(x), 0.25); + } + +} +class Rosen extends AbstractObjectiveFunction { + @Override + public double valueOf (double[] x) { + double res = 0; + for (int i = 0; i < x.length-1; ++i) + res += 1e2 * (x[i]*x[i] - x[i+1]) * (x[i]*x[i] - x[i+1]) + + (x[i] - 1.) * (x[i] - 1.); + return res; + } +} + +class Ackley extends AbstractObjectiveFunction { + double axisratio = 1.; + Ackley(double axra) { + axisratio = axra; + } + public Ackley() { + } + @Override + public double valueOf (double[] x) { + double res = 0; + double res2 = 0; + double fac = 0; + for (int i = 0; i < x.length; ++i) { + fac = Math.pow(axisratio, (i-1.)/(x.length-1.)); + res += fac * fac * x[i]*x[i]; + res2 += Math.cos(2. * Math.PI * fac * x[i]); + } + return (20. - 20. * Math.exp(-0.2 * Math.sqrt(res/x.length)) + + Math.exp(1.) - Math.exp(res2/x.length)); + } +} +class Rastrigin extends AbstractObjectiveFunction { + Rastrigin() { + this(1, 10); + } + Rastrigin(double axisratio, double amplitude) { + this.axisratio = axisratio; + this.amplitude = amplitude; + } + public double axisratio = 1; + public double amplitude = 10; + @Override + public double valueOf (double[] x) { + double fac; + double res = 0; + for (int i = 0; i < x.length; ++i) { + fac = Math.pow(axisratio,(i-1.)/(x.length-1.)); + if (i == 0 && x[i] < 0) + fac *= 1.; + res += fac * fac * x[i] * x[i] + + amplitude * (1. - Math.cos(2.*Math.PI * fac * x[i])); + } + return res; + } +} +/* Template fitness function +class fff extends AbstractObjectiveFunction { + public double valueOf(double[] x) { + double res = 0; + for (int i = 0; i < x.length; ++i) { + } + return res; + } +} +*/ + +class Basis { + double [][] B; // usually field names should be lower case + Random rand = new Random(2); // use not always the same basis + + double[] Rotate(double[] x) { + GenBasis(x.length); + double[] y = new double[x.length]; + for (int i = 0; i < x.length; ++i) { + y[i] = 0; + for (int j = 0; j < x.length; ++j) + y[i] += B[i][j] * x[j]; + } + return y; + } + double[][] Rotate(double[][] pop) { + double[][] y = new double[pop.length][]; + for (int i = 0; i < pop.length; ++i) { + y[i] = Rotate(pop[i]); + } + return y; + } + + void GenBasis(int DIM) + { + if (B != null ? B.length == DIM : false) + return; + + double sp; + int i,j,k; + + /* generate orthogonal basis */ + B = new double[DIM][DIM]; + for (i = 0; i < DIM; ++i) { + /* sample components gaussian */ + for (j = 0; j < DIM; ++j) + B[i][j] = rand.nextGaussian(); + /* substract projection of previous vectors */ + for (j = i-1; j >= 0; --j) { + for (sp = 0., k = 0; k < DIM; ++k) + sp += B[i][k]*B[j][k]; /* scalar product */ + for (k = 0; k < DIM; ++k) + B[i][k] -= sp * B[j][k]; /* substract */ + } + /* normalize */ + for (sp = 0., k = 0; k < DIM; ++k) + sp += B[i][k]*B[i][k]; /* squared norm */ + for (k = 0; k < DIM; ++k) + B[i][k] /= Math.sqrt(sp); + } + } +} diff --git a/src/main/java/fr/inria/optimization/cmaes/fitness/IObjectiveFunction.java b/src/main/java/fr/inria/optimization/cmaes/fitness/IObjectiveFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..60041dc174d6ab79dc5bc5597a0ad9ba60f747b2 --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/fitness/IObjectiveFunction.java @@ -0,0 +1,12 @@ +package fr.inria.optimization.cmaes.fitness; + +/** Minimalistic interface of a single-objective function (fitness function) to be minimized. +*/ +public interface IObjectiveFunction { + /** @param x a point (candidate solution) in the pre-image of the objective function + @return objective function value of the input search point + */ + double valueOf(double x[]); + boolean isFeasible(double x[]); +} + diff --git a/src/main/java/fr/inria/optimization/cmaes/fitness/IObjectiveFunctionParallel.java b/src/main/java/fr/inria/optimization/cmaes/fitness/IObjectiveFunctionParallel.java new file mode 100644 index 0000000000000000000000000000000000000000..14e16b070d95a578a0843617ab3eeb46b38b7a95 --- /dev/null +++ b/src/main/java/fr/inria/optimization/cmaes/fitness/IObjectiveFunctionParallel.java @@ -0,0 +1,17 @@ +package fr.inria.optimization.cmaes.fitness; + +/** Interface to a single-objective function to be minimized, + * that accepts an array + * of points double[][], a population to be evaluated within one call to method valuesOf(). +*/ +public interface IObjectiveFunctionParallel { + + /** + * @param pop is an array of search points to be evaluated, where + * pop[i] is the i-th point. + * @return array of objective function values. The i-th value + * is the objective function value of pop[i]. + * */ + double[] valuesOf(double pop[][]); +} + diff --git a/src/main/java/miscellaneous/Miscellaneous.java b/src/main/java/miscellaneous/Miscellaneous.java index 3ae367a78cd7e1b78f1e4ccdf9401fdd86dee2ee..8b364010cfc3c6b1eb17e324165dc0c8d60db048 100644 --- a/src/main/java/miscellaneous/Miscellaneous.java +++ b/src/main/java/miscellaneous/Miscellaneous.java @@ -40,7 +40,7 @@ public class Miscellaneous { if (T <= Tmin || T >= Tmax) { return 0; } else { - return (T - Tmin) * (T - Tmax) / ((T - Tmin) * (T - Tmax) - Math.pow(T - Topt, 2.)); + return (T - Tmin) * (T - Tmax) / ((T - Tmin) * (T - Tmax) - ((T - Topt) * (T-Topt))); } } } diff --git a/src/main/java/species/Calibrate.java b/src/main/java/species/Calibrate.java new file mode 100644 index 0000000000000000000000000000000000000000..171ff54dfabcadbb1bf6db4152c487713701a554 --- /dev/null +++ b/src/main/java/species/Calibrate.java @@ -0,0 +1,297 @@ +/** + * patrick + * @author Patrick Lambert + * @copyright Copyright (c) 2018, Irstea + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ +package species; + + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Map; + +import fr.cemagref.simaqualife.extensions.pilot.BatchRunner; +import fr.cemagref.simaqualife.pilot.Pilot; +import fr.inria.optimization.cmaes.CMAEvolutionStrategy; +import fr.inria.optimization.cmaes.fitness.IObjectiveFunction; +import miscellaneous.Duo; +import miscellaneous.ReflectUtils; + +/** + * + */ +public class Calibrate { + + + + public Calibrate() { + + } + + + + public static void main(String[] args) { + GR3DObjeciveFunction fitfun = new GR3DObjeciveFunction(10.,10.); + + // new a CMA-ES and set some initial values + CMAEvolutionStrategy cma = new CMAEvolutionStrategy(); + + cma.setDimension(fitfun.getParameterRanges().size()); + + //cma. parameters.setPopulationSize(30); + cma.setInitialX(5.); + cma.setInitialStandardDeviation(2.5); + + cma.options.stopTolFun=1e-6; // function value range within iteration and of past values + + // from CMAEvolutionStrategy.properties, to avoid to load the file + cma.options.stopTolFunHist = 1e-13 ; // function value range of 10+30*N/lambda past values + cma.options.stopTolX = 0.0 ; // absolute x-changes + cma.options.stopTolXFactor = 1e-11; // relative to initial stddev + cma.options.stopTolUpXFactor = 1000; // relative to initial stddev + + + // initialize cma and get fitness array to fill in later + double[] fitness = cma.init(); // new double[cma.parameters.getPopulationSize()]; + + double[][] pop= cma.samplePopulation(); + // iteration loop + while(cma.stopConditions.getNumber() == 0) { + + // --- core iteration step --- + pop = cma.samplePopulation(); // get a new population of solutions + + for(int i = 0; i < pop.length; ++i) { // for each candidate solution i + // a simple way to handle constraints that define a convex feasible domain + // (like box constraints, i.e. variable boundaries) via "blind re-sampling" + // assumes that the feasible domain is convex, the optimum is + while (!fitfun.isFeasible(pop[i])) // not located on (or very close to) the domain boundary, + pop[i] = cma.resampleSingle(i); // initialX is feasible and initialStandardDeviations are + // sufficiently small to prevent quasi-infinite looping here + // compute fitness/objective value + fitness[i] = fitfun.valueOf(pop[i]); // fitfun.valueOf() is to be minimized + } + cma.updateDistribution(fitness); // pass fitness array to update search distribution + // --- end core iteration step --- + + // output to files and console + cma.writeToDefaultFiles(0); + int outmod = 100; + if (cma.getCountIter() % (100*outmod) == 1) + cma.printlnAnnotation(); // might write file as well + if (cma.getCountIter() % outmod == 1) + cma.println(); + // + // evaluate mean value as it is the best estimator for the optimum + cma.setFitnessOfMeanX(fitfun.valueOf(cma.getMeanX())); // updates the best ever solution + } + + // final output + //cma.writeToDefaultFiles(1); + cma.println(); + cma.println("Terminated due to"); + for (String s : cma.stopConditions.getMessages()) + cma.println(" " + s); + + cma.println("Best function value " + cma.getBestFunctionValue() + " at evaluation " + cma.getBestEvaluationNumber()); + + cma.println("best par: "+ Arrays.toString(fitfun.x2par(cma.getBestX()))); + + System.out.println("\n"+fitfun.valueOf(cma.getBestX())); + System.out.println(); + for (int i=0; i < pop.length; i++) { + System.out.println(Arrays.toString(fitfun.x2par((pop[i])))); + } + } +} + + +/** + * objective function based on GR3D + */ +class GR3DObjeciveFunction implements IObjectiveFunction { + + private double a_femaleLengthPenalty=100.; + private double a_maleLengthPenalty=100.; + private Map<String, Duo<Double, Double>> parameterRanges; + + private transient Pilot pilot; + + public GR3DObjeciveFunction(double a_femaleLengthPenalty, double a_maleLengthPenalty) { + loadSimulation(); + this.a_femaleLengthPenalty = a_femaleLengthPenalty; + this.a_maleLengthPenalty = a_maleLengthPenalty; + + parameterRanges = new Hashtable<String, Duo<Double,Double>>(); + parameterRanges.put("tempMinRep", new Duo<Double, Double>(9., 12.)); + parameterRanges.put("KOptFemale", new Duo<Double, Double>(0.2, .5)); + parameterRanges.put("KOptMale", new Duo<Double, Double>(0.2, .5)); + } + + + + /** + * @return the parameterRanges + */ + public Map<String, Duo<Double, Double>> getParameterRanges() { + return parameterRanges; + } + + + @Override + public double valueOf(double[] x) { + // x[0] tempMinRep + // x[1] kOptFemale + // x[2] kOptMale + + double[] par = x2par(x); // in natural unit + try { + pilot.load(); + + ReflectUtils.setFieldValueFromPath(pilot, "aquaticWorld.aquaNismsGroupsList.0.processes.processesEachStep.6.tempMinRep", par[0]); + //System.out.println("tempMinRep: " + (double) ReflectUtils.getValueFromPath(pilot, "aquaticWorld.aquaNismsGroupsList.0.processes.processesEachStep.6.getTempMinRep")); + + ReflectUtils.setFieldValueFromPath(pilot, "aquaticWorld.aquaNismsGroupsList.0.processes.processesEachStep.3.kOptForFemale", par[1]); + //System.out.println("KOptFemale: " + (double) ReflectUtils.getValueFromPath(pilot, "aquaticWorld.aquaNismsGroupsList.0.processes.processesEachStep.3.getkOptForFemale")); + + ReflectUtils.setFieldValueFromPath(pilot, "aquaticWorld.aquaNismsGroupsList.0.processes.processesEachStep.3.kOptForMale",par[2]); + //System.out.println("KOptMale: " + (double) ReflectUtils.getValueFromPath(pilot, "aquaticWorld.aquaNismsGroupsList.0.processes.processesEachStep.3.getkOptForMale")); + + } catch (Exception e1) { + e1.printStackTrace(); + } + + pilot.run(); + + double likelihood =0; + double femaleLengthPenalty = 0.; + double maleLengthPenalty =0.; + try { + + likelihood = (double) ReflectUtils.getValueFromPath(pilot, "aquaticWorld.aquaNismsGroupsList.0.computeLikelihood"); + //System.out.println("likelihood: "+ likelihood); + femaleLengthPenalty = (double) ReflectUtils.getValueFromPath(pilot, "aquaticWorld.aquaNismsGroupsList.0.computeFemaleSpawnerForFirstTimeSummaryStatistic"); + //System.out.println("femaleLengthPenalty: "+femaleLengthPenalty); + maleLengthPenalty = (double) ReflectUtils.getValueFromPath(pilot, "aquaticWorld.aquaNismsGroupsList.0.computeMaleSpawnerForFirstTimeSummaryStatistic"); + //System.out.println("maleLengthPenalty: "+maleLengthPenalty); + + System.out.println("likelihood: "+ likelihood+ " femaleLengthPenalty: "+femaleLengthPenalty+ " maleLengthPenalty: "+maleLengthPenalty); + + } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + double result = likelihood +a_femaleLengthPenalty * femaleLengthPenalty + a_maleLengthPenalty * maleLengthPenalty ; + + System.out.println(Arrays.toString(x2par(x)) + "->"+result); + return result; + } + + @Override + public boolean isFeasible(double[] x) { + double[] par = x2par(x); + + boolean test = true; + double a, b; + a = parameterRanges.get("tempMinRep").getFirst(); + b = parameterRanges.get("tempMinRep").getSecond(); + if (par[0] < a | par[0]>b) + test = false; + + a = parameterRanges.get("KOptFemale").getFirst(); + b = parameterRanges.get("KOptFemale").getSecond(); + if (par[1] < a | par[1]>b) + test = false; + + a = parameterRanges.get("KOptMale").getFirst(); + b = parameterRanges.get("KOptMale").getSecond(); + if (par[2] < a | par[2]>b) + test = false; + + return test; + } + + public void loadSimulation() { + String[] args= {"-simDuration", "100", "-simBegin", "1", "-RNGStatusIndex", "1", + "-groups", "data/input/fishTryRealBV_calibration.xml", + "-env", "data/input/BNtryRealBasins.xml", + "q", "true" + }; + pilot = new Pilot(); + + try { + // iniatialize the simulation + pilot.init(); + // no display on the console ???? + pilot.setQuiet(); + + new BatchRunner(pilot).parseArgs(args, true, true, false); + } catch (IOException | IllegalArgumentException e) { + + e.printStackTrace(); + } + } + + /** + * @param x paremters in the range [0,10] + * @return parameter in natural unit (based on parameters range) + */ + public double[] x2par(double[] x) { + double[] naturalPar = new double[x.length]; // par in natural unit + double a, b; + + a = parameterRanges.get("tempMinRep").getFirst(); + b = parameterRanges.get("tempMinRep").getSecond(); + naturalPar[0] = a + (b-a) * x[0] /10.; + + a = parameterRanges.get("KOptFemale").getFirst(); + b = parameterRanges.get("KOptFemale").getSecond(); + naturalPar[1] = a + (b-a) * x[1] /10.; + + a = parameterRanges.get("KOptMale").getFirst(); + b = parameterRanges.get("KOptMale").getSecond(); + naturalPar[2] = a + (b-a) * x[2] /10.; + + return naturalPar; + } + + + public double[] par2x(double[] naturalPar) { + double[] x = new double[naturalPar.length]; + double a, b; + + a = parameterRanges.get("tempMinRep").getFirst(); + b = parameterRanges.get("tempMinRep").getSecond(); + x[0] = 10.* (naturalPar[0] - a) / (b-a); + + a = parameterRanges.get("KOptFemale").getFirst(); + b = parameterRanges.get("KOptFemale").getSecond(); + x[1] = 10.* (naturalPar[1] - a) / (b-a); + + a = parameterRanges.get("KOptFemale").getFirst(); + b = parameterRanges.get("KOptFemale").getSecond(); + x[2] = 10.* (naturalPar[2] - a) / (b-a); + + return x; + } +} + + + diff --git a/src/main/java/species/DiadromousFish.java b/src/main/java/species/DiadromousFish.java index bec490f285042bce16328d16acfcc692d37f9269..002042070fdc641407aa8fe205edd719416906b3 100644 --- a/src/main/java/species/DiadromousFish.java +++ b/src/main/java/species/DiadromousFish.java @@ -9,9 +9,14 @@ import fr.cemagref.simaqualife.pilot.Pilot; public class DiadromousFish extends AquaNism<Basin, BasinNetwork> { public static enum Stage {IMMATURE, MATURE}; + //TODO replace UNDIFFERENCIED by UNDETERMINED public static enum Gender {UNDIFFERENCIED, FEMALE, MALE}; private long amount; + /** + * age of the fish + * @unit (decimal) year + */ private double age; private double length; private Basin birthBasin; @@ -19,13 +24,6 @@ public class DiadromousFish extends AquaNism<Basin, BasinNetwork> { private Gender gender; private int numberOfReproduction; - public DiadromousFish(Pilot pilot, Basin position, double initialLength, long fishAmount) { - this(pilot, position, initialLength, fishAmount, Gender.UNDIFFERENCIED); - } - - public DiadromousFish(Pilot pilot, Basin position, double initialLength) { - this(pilot, position, initialLength, 1, Gender.UNDIFFERENCIED); - } public DiadromousFish(Pilot pilot, Basin position, double initialLength, long fishAmount, Gender gender) { super(pilot, position); @@ -108,6 +106,7 @@ public class DiadromousFish extends AquaNism<Basin, BasinNetwork> { return gender; } + @Override public <ANG extends AquaNismsGroup<?, BasinNetwork>> void moveTo( Pilot pilot, Basin destination, ANG group) { diff --git a/src/main/java/species/DiadromousFishGroup.java b/src/main/java/species/DiadromousFishGroup.java index b84dbb45c9f52904b942bafe1a7e3d48517e1af7..185f758d4e4b4350123f95ec0d875149c8a447e2 100644 --- a/src/main/java/species/DiadromousFishGroup.java +++ b/src/main/java/species/DiadromousFishGroup.java @@ -13,11 +13,15 @@ import fr.cemagref.simaqualife.kernel.Processes; import fr.cemagref.simaqualife.pilot.Pilot; import java.awt.Color; +import java.io.BufferedWriter; +import java.io.File; import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; +import java.util.Hashtable; import java.util.List; import java.util.Locale; import java.util.Map; @@ -28,50 +32,319 @@ import java.util.regex.Pattern; import miscellaneous.Duo; import miscellaneous.TreeMapForCentile; +import species.DiadromousFish.Gender; import org.openide.util.lookup.ServiceProvider; +/** + * + */ @ServiceProvider(service = AquaNismsGroup.class) public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNetwork> implements Comparable<DiadromousFishGroup> { public String name = "species A"; public Color color = Color.RED; - public double linfVonBert = 60.; + + /** + * distance maximum of dispersion + * @unit km + */ public double dMaxDisp = 300.; - public double lFirstMaturity = 40.; + + /** + * Routine to compute nutrient fluxes operated by a single individual (TODO by a single super individual). + * + */ + private NutrientRoutine nutrientRoutine; + public String fileNameInputForInitialObservation = "data/input/reality/Obs1900.csv"; + + /** + * centile to calcucale the range of species distribution + * @unit + */ public double centileForRange = 0.95; + /** + * file with the calibated parameters (from baysian approach) + * @unit + */ private String parameterSetfileName= "data/input/reality/parameterSet.csv"; + + /** + * line to use in the calibrated parameters file + * @unit + */ private int parameterSetLine =0; + /** + * year when the update of the basin should occur + * @unit + */ private long yearOfTheUpdate; + + /** + * list of the basins to be updated + * column 1: name of the basin + * column 2: Pattractive: how the bassin become attractive (0 not attractive, 1 ??? normal weight associated to catchment size) + * column 3: Paccessible: how the bassin become acesssible (0 not accessible, 1 ???normal weight to inter catchment distance ) + * @unit + */ private String basinsToUpdateFile = "data/input/reality/basinsToUpdate.csv"; private String outputPath = "data/output/"; + private String fileNameFluxes = "fluxes"; + + private transient BufferedWriter bWForFluxes; + private transient String sep; + + /** + * map + * <key> basin name + * <value> Duo + * <first> pAttractive + * <second> pAccessible + * @unit + */ private transient Map<String, Duo<Double, Double>> basinsToUpdate; - private transient double kOpt; //parametre de croissance + + /** + * length when fish hatchs ( when the diadromousFish is created after reproduction) + * no diffrence between gender + * @unit cm + */ + private double lengthAtHatching = 2.; + + /** + * L infinity of the van Bertalanffy growth curve for female + * L = Linf *(1-exp(-K*(t-t0)) + * @unit cm + */ + public double linfVonBertForFemale = 60.; + + /** + * L infinity of the van Bertalanffy growth curve for male + * L = Linf *(1-exp(-K*(t-t0)) + * @unit cm + */ + public double linfVonBertForMale = 60.; + + + /** + * Brody growth coefficient of the von Bertalanffy growth curve for female (calculated from the parameterset file) + * * L = Linf *(1-exp(-K*(t-t0)) + * @unit year-1 + */ + private transient double kOptForFemale; + + /** + * Brody growth coefficient of the von Bertalanffy growth curve for male (calculated from the parameterset file) + * * L = Linf *(1-exp(-K*(t-t0)) + * @unit year-1 + */ + private transient double kOptForMale; + + /** + * length at first maturity. At that length the female become Stage.MATURE + * @unit cm + */ + public double lFirstMaturityForFemale = 55.; + + + /** + * length at first maturity. At that length the female become Stage.MATURE + * @unit cm + */ + public double lFirstMaturityForMale = 40.; + + + /** + * minimum temperature for the reproduction (from the parameterset file) + * @unit °C + */ private transient double tempMinRep; //parametre de reproduction + + + /** + * list of the parameters provided by the calibration + * @unit + */ private transient List<Duo<Double, Double>> parameterSets; + // ================================================= + // calibration + // ================================================= + + private Double targetedAgeForFemaleSpawnerForFirstTime = 5.5; + private Double targetedAgeForMaleSpawnerForFirstTime = 4.5; + + /** + * map of observedoccurence in 1900 + * @unit + */ + private transient Map<String, Integer> obs1900 ; + public static void main(String[] args) { - System.out.println((new XStream(new DomDriver())).toXML(new DiadromousFishGroup(new Pilot(), null, null))); + DiadromousFishGroup diadromousFishGroup = new DiadromousFishGroup(new Pilot(), null, null); + + double aResidenceTime =30; + + + Map <String, Double> anExcretionRate = new Hashtable <String, Double>(); + anExcretionRate.put("N", 24.71E-6); //values from Barber et al, Alosa sapidissima in ug/g wet mass/hour : convertit en g + anExcretionRate.put("P", 2.17E-6); //values from Barber et al, Alosa sapidissima in ug/g wet mass/hour: convertit en g + + + /* + * A feature pre spawning + */ + Map<DiadromousFish.Gender, Map<String, Double>> aFeaturePreSpawning = new Hashtable<DiadromousFish.Gender, Map<String,Double>>(); + + /* + * For females + */ + Map<String,Double> aFeature = new Hashtable<String,Double>(); + + aFeature.put("aLW", Math.exp(-4.9078)); //weight size relationship computed from BDalosesBruch + aFeature.put("bLW", 3.147); + //aFeature.put("bLW",3.3429);// parametre "b" de la relation taille/poids - Coefficient d'allometrie + //aFeature.put("aLW",1.2102E-6 * Math.pow(10., aFeature.get("bLW"))); // parametre "a" de la relation taille/poids en kg/cm- Traduit la condition + //aFeature.put("GSI",0.15); + aFeaturePreSpawning.put(Gender.FEMALE, aFeature); + + /* + * For males + */ + aFeature = new Hashtable<String,Double>(); + aFeature.put("aLW", Math.exp(-1.304)); + aFeature.put("bLW", 2.1774); + //aFeature.put("aLW",2.4386E-6 * Math.pow(10, aFeature.get("bLW"))); // Conversion des g/mm en g.cm (from Taverny, 1991) + //aFeature.put("GSI",.08); + aFeaturePreSpawning.put(Gender.MALE,aFeature); + + + /* + * a Feature post Spawning + */ + Map<DiadromousFish.Gender, Map<String, Double>> aFeaturePostSpawning = new Hashtable<DiadromousFish.Gender, Map<String,Double>>(); + + /* + * For females + */ + aFeature = new Hashtable<String,Double>(); + aFeature.put("aLW", Math.exp(-4.3276)); //weight size relationship computed from BDalosesBruch + aFeature.put("bLW", 2.9418); + //aFeature.put("GSI",0.10); //From BDalosesBruch + //aFeature.put("aLW",aFeaturePreSpawning.get(Gender.FEMALE).get("aLW")/(1+aFeature.get("GSI"))); // parametre "a" de la relation taille/poids avec Lt en cm - Traduit la condition + //aFeature.put("bLW",aFeaturePreSpawning.get(Gender.FEMALE).get("bLW"));// parametre "b" de la relation taille/poids - Coefficient d'allometrie + aFeaturePostSpawning.put(Gender.FEMALE, aFeature); + + /* + * For males + */ + aFeature = new Hashtable<String,Double>(); + + aFeature.put("aLW", Math.exp(-4.5675));// parametre "a" de la relation taille/poids - Coefficient d'allometrie + aFeature.put("bLW", 2.9973); + //aFeature.put("GSI",.05); From BDalosesBruch + //aFeature.put("aLW",aFeaturePreSpawning.get(Gender.MALE).get("aLW")/(1+aFeature.get("GSI"))); + //aFeature.put("bLW",aFeaturePreSpawning.get(Gender.MALE).get("bLW")); + aFeaturePostSpawning.put(Gender.MALE,aFeature); + + + Map<DiadromousFish.Gender, Double> aGameteSpawned = new Hashtable <DiadromousFish.Gender,Double>(); + aGameteSpawned.put(Gender.FEMALE, 131.); // Compute from the difference between spawned and unspawned ovaries ie correspond to a mean weight of eggs spawned + aGameteSpawned.put(Gender.MALE, 44.8); // Compute from the difference between spawned and unspawned testes ie correspond to a mean weight of sperm spawned + + + // carcass composition for fish before spawning + Map<DiadromousFish.Gender, Map<String, Double>> aCompoCarcassPreSpawning = new Hashtable<DiadromousFish.Gender,Map<String,Double>>(); + Map<String,Double> aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 2.958 / 100.); //On remplit une collection avec un put. + aCompo.put("P", 0.673 / 100.); + aCompoCarcassPreSpawning.put(Gender.FEMALE,aCompo); + + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 2.941 / 100.); + aCompo.put("P", 0.666 / 100.); + aCompoCarcassPreSpawning.put(Gender.MALE,aCompo); + + + + // carcass composition for fish after spawning + Map<DiadromousFish.Gender, Map<String, Double>> aCompoCarcassPostSpawning = new Hashtable<DiadromousFish.Gender,Map<String,Double>>(); + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 3.216 / 100.); //On remplit une collection avec un put. + aCompo.put("P", 0.997 / 100.); + aCompoCarcassPostSpawning.put(Gender.FEMALE,aCompo); + + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 2.790 / 100.); // From Haskel et al, 2017 + aCompo.put("P", 0.961 / 100.); + aCompoCarcassPostSpawning.put(Gender.MALE,aCompo); + + + + // Gametes composition approximated by the difference between gonads weight before and after spawning. + Map<DiadromousFish.Gender, Map<String, Double>> aCompoGametes = new Hashtable<DiadromousFish.Gender,Map<String,Double>>(); + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 3.242 / 100.); //On remplit une collection avec un put. From Haskel et al, 2018. + aCompo.put("P", 0.320 / 100.); // Haskel = %P, N, ici ratio donc divise par 100 + aCompoGametes.put(Gender.FEMALE,aCompo); + + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 3.250 / 100.); + aCompo.put("P", 0.724 / 100.); + aCompoGametes.put(Gender.MALE,aCompo); + + + // features for juveniles + + Map<String,Double> aJuvenileFeatures = new Hashtable<String, Double>(); + aJuvenileFeatures.put("bLW",3.0306); + aJuvenileFeatures.put("aLW",Math.exp(-11.942) * Math.pow(10., aJuvenileFeatures.get("bLW"))); + + + // carcass composition for juveniles fish + Map<String, Double> aCompoJuveniles = new Hashtable<String,Double>(); + aCompoJuveniles.put("N", 2.803 / 100.); //On remplit une collection avec un put. %N in wet weight (Haskell et al, 2017) on Alosa sapidissima + aCompoJuveniles.put("P", 0.887 / 100.); //%P in wet weight (from Haskell et al, 2017) on Alosa sapidissima + + + ArrayList <String> nutrientsOfInterest= new ArrayList <String>(); + nutrientsOfInterest.add("N"); + nutrientsOfInterest.add("P"); + + + diadromousFishGroup.nutrientRoutine = new NutrientRoutine(nutrientsOfInterest,aResidenceTime, anExcretionRate, aFeaturePreSpawning, aFeaturePostSpawning, aCompoCarcassPreSpawning, aCompoCarcassPostSpawning, + aCompoGametes, aJuvenileFeatures, aCompoJuveniles); + + + System.out.println((new XStream(new DomDriver())).toXML(diadromousFishGroup)); } public DiadromousFishGroup(Pilot pilot, BasinNetwork environment, Processes processes) { super(pilot, environment, processes); } + public DiadromousFishGroup() { + super(); + } public double getPattractive(String basinName){ - if (basinsToUpdate.containsKey(basinName.substring(0, basinName.length()-2))) - return basinsToUpdate.get(basinName.substring(0, basinName.length()-2)).getFirst(); + // TODO pass in argument a Basin + // remove "-s" of the sea basin name + String shortBasinName = basinName.substring(0, basinName.length()-2); + if (basinsToUpdate.containsKey(shortBasinName)) + return basinsToUpdate.get(shortBasinName).getFirst(); else return Double.NaN; } public double getPaccessible(String basinName){ + // TODO pass in argument a Basin + //WHY not a short name if (basinsToUpdate.containsKey(basinName)) return basinsToUpdate.get(basinName).getSecond(); else @@ -95,6 +368,7 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe InvocationTargetException { super.initTransientParameters(pilot); + // basin to be updated if ( basinsToUpdate != null){ String subDir=basinsToUpdateFile; if (basinsToUpdateFile.lastIndexOf("/")!=-1) @@ -137,8 +411,9 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe } // charge kopt et temMinRep depuis le fichier de parametre. Sinon (parameterSetLine<=0), ce sont les - // valeur dasn le procoessus de reroduction qui sont utilis� - kOpt=Double.NaN; + // valeurs dans le processus de reproduction qui sont utilis� + kOptForFemale=Double.NaN; + kOptForMale=Double.NaN; tempMinRep =Double.NaN; if (parameterSetLine>0){ parameterSets = new ArrayList<Duo<Double,Double>>(10); @@ -171,56 +446,153 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe e.printStackTrace(); } - kOpt = parameterSets.get(parameterSetLine-1).getFirst(); + double kOpt = parameterSets.get(parameterSetLine-1).getFirst(); + // 40 correspond to the lFirstMaturity used by Rougier to calibrate the model + kOptForMale = kOpt *lFirstMaturityForMale / 40.; + kOptForFemale= kOpt * lFirstMaturityForFemale / 40.; tempMinRep = parameterSets.get(parameterSetLine-1).getSecond(); } + + // open an bufferad writer to export fluxes + if (fileNameFluxes != null){ + sep = ";"; + new File(this.outputPath +fileNameFluxes).getParentFile().mkdirs(); + try { + bWForFluxes = new BufferedWriter(new FileWriter(new File(this.outputPath+ + fileNameFluxes +this.getSimulationId() + ".csv"))); + + bWForFluxes.write("timestep"+sep+"year"+sep+"season"+sep+"basin" + +sep+"abundance" + sep + "fluxType"+ sep + "origin" +sep+"biomass"); + for (String nutrient : nutrientRoutine.getNutrientsOfInterest()) { + bWForFluxes.write(sep+nutrient); + } + bWForFluxes.write("\n"); + + } catch (IOException e) { + e.printStackTrace(); + } + } + // ------------------------------------------------------------------ + // read occurence data file + // ----------------------------------------------------------- + FileReader reader; + Scanner scanner; + + obs1900 = new HashMap<String, Integer>(); + try { + reader = new FileReader(fileNameInputForInitialObservation); + // Parsing the file + scanner = new Scanner(reader); + scanner.useLocale(Locale.ENGLISH); // to have a comma as decimal separator !!! + scanner.useDelimiter(Pattern.compile("[;\r]")); + + scanner.nextLine(); // to skip the file first line of entete + + while (scanner.hasNext()) { + obs1900.put(scanner.next().replaceAll("\n", ""), scanner.nextInt()); + } + reader.close(); + scanner.close(); + + } catch (IOException ex) { + Logger.getLogger(DiadromousFishGroup.class.getName()).log(Level.SEVERE, null, ex); + } + + // defaut values + if (targetedAgeForFemaleSpawnerForFirstTime == null) + targetedAgeForFemaleSpawnerForFirstTime = 5.5; + if (targetedAgeForMaleSpawnerForFirstTime == null) + targetedAgeForMaleSpawnerForFirstTime = 4.5; } - public double getKOpt(){ + + + /** + * @param fish + * @param group + * @return the Brody coeff + */ + public double getKOpt(DiadromousFish fish) { + double kOpt = 0.; + + if (fish.getGender() == Gender.FEMALE) + kOpt = kOptForFemale; + else if (fish.getGender() == Gender.MALE) + kOpt = kOptForMale; + else + kOpt= (kOptForFemale + kOptForMale) / 2.; + return kOpt; } + public BufferedWriter getbWForFluxes() { + return bWForFluxes; + } + public double getTempMinRep(){ return tempMinRep; } + @Observable(description = "Nb of SI") public int getNbSI() { - return this.getAquaNismsList().size(); + int nbSI = 0; + for (Basin basin : this.getEnvironment().getBasins() ) { + if (basin.getFishs(this) != null) + nbSI += basin.getFishs(this).size(); + } + return nbSI; } + @Observable(description = "Sizes mean of SI") public double getSizesMeanOfSI() { - double sum = 0; - for (DiadromousFish list : getAquaNismsList()) { - sum += (int) list.getAmount(); + double totalEffective = 0; + double nbSI =0; + for (Basin basin : this.getEnvironment().getBasins() ) { + if (basin.getFishs(this) != null) { + nbSI += basin.getFishs(this).size(); + for (DiadromousFish superFish : basin.getFishs(this)) { + totalEffective += superFish.getAmount(); + } + } } - return sum / getAquaNismsList().size(); + if (nbSI >=0) + return totalEffective /nbSI; + else + return Double.NaN; } + @Observable(description = "# of SI with ind < 10") public double getNbLittleSI() { double nb = 0; - for (DiadromousFish list : getAquaNismsList()) { - if (list.getAmount() < 10) { - nb++; + for (Basin basin : this.getEnvironment().getBasins() ) { + if (basin.getFishs(this) != null) { + for (DiadromousFish superFish : basin.getFishs(this)) { + if ( superFish.getAmount()<10L) + nb++; + } } } return nb; } + public double getMeanLengthOfMatureFish(){ - double meanLengthOfMatureFish = 0.; double sumOfLength = 0.; double numberOfMatureFish = 0.; - for (DiadromousFish fish : getAquaNismsList()){ - if (fish.isMature()){ - sumOfLength += fish.getAmount() * fish.getLength(); - numberOfMatureFish += fish.getAmount(); + for (Basin basin : this.getEnvironment().getBasins() ) { + if (basin.getFishs(this) != null) { + for (DiadromousFish fish : basin.getFishs(this)) { + if (fish.isMature()){ + sumOfLength += fish.getAmount() * fish.getLength(); + numberOfMatureFish += fish.getAmount(); + } + } } - meanLengthOfMatureFish = sumOfLength / numberOfMatureFish; } - return meanLengthOfMatureFish; + return sumOfLength / numberOfMatureFish; } public double getStandardDeviationOfMatureFishLength(){ @@ -228,13 +600,18 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe double sumOfSquareLength = 0.; double numberOfMatureFish = 0.; double meanOfSquareLengthOfMatureFish = 0.; - for (DiadromousFish fish : getAquaNismsList()){ - if (fish.isMature()){ - sumOfSquareLength += fish.getAmount() * Math.pow(fish.getLength(), 2); - numberOfMatureFish += fish.getAmount(); + for (Basin basin : this.getEnvironment().getBasins() ) { + if (basin.getFishs(this) != null) { + for (DiadromousFish fish : basin.getFishs(this)) { + if (fish.isMature()){ + sumOfSquareLength += fish.getAmount() * fish.getLength() * fish.getLength(); + numberOfMatureFish += fish.getAmount(); + } + } } meanOfSquareLengthOfMatureFish = sumOfSquareLength / numberOfMatureFish; - standardDeviationOfMatureFishLength = Math.pow((meanOfSquareLengthOfMatureFish - Math.pow(getMeanLengthOfMatureFish(), 2)), 0.5); + double meanLength = getMeanLengthOfMatureFish(); + standardDeviationOfMatureFishLength = Math.sqrt(meanOfSquareLengthOfMatureFish - meanLength * meanLength); } return standardDeviationOfMatureFishLength; } @@ -247,78 +624,138 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe return color; } - public double getLinfVonBert() { - return linfVonBert; + public double getLinfVonBert(DiadromousFish fish) { + if ( fish.getGender() == Gender.FEMALE) + return linfVonBertForFemale; + else if (fish.getGender() == Gender.MALE) + return linfVonBertForMale; + else + return (linfVonBertForFemale+linfVonBertForMale)/2.; } - public void setLinfVonBert(double linfVonBert) { - this.linfVonBert = linfVonBert; + + public double getlFirstMaturity(DiadromousFish fish) { + if ( fish.getGender() == Gender.FEMALE) + return lFirstMaturityForFemale; + else if (fish.getGender() == Gender.MALE) + return lFirstMaturityForMale; + else + return (lFirstMaturityForFemale+lFirstMaturityForFemale)/2.; } + + /** + * @return the lengthAtHatching + */ + public double getLengthAtHatching() { + return lengthAtHatching; + } + + public double getdMaxDisp() { return dMaxDisp; } - public double getlFirstMaturity() { - return lFirstMaturity; + public NutrientRoutine getNutrientRoutine() { + return nutrientRoutine; } - public void setlFirstMaturity(double lFirstMaturity) { - this.lFirstMaturity = lFirstMaturity; + + // ================================================================ + // statictis for calibration + // ================================================================ + public double computeFemaleSpawnerForFirstTimeSummaryStatisticWithTarget(double TARGET) { + double sum = 0; + for (RiverBasin riverBasin : getEnvironment().getRiverBasins()) { + if (riverBasin.getSpawnersForFirstTimeMeanAges(Gender.FEMALE).getMeanWithoutZero() > 0.) { + double val = riverBasin.getSpawnersForFirstTimeMeanAges(Gender.FEMALE).getMeanWithoutZero() - TARGET; + sum += val * val; + } + } + //System.out.println("sum female: " + sum); + return sum; } + @Observable(description="Female spawners For First Time Summary Statistic") + public double computeFemaleSpawnerForFirstTimeSummaryStatistic() { + return computeFemaleSpawnerForFirstTimeSummaryStatisticWithTarget(targetedAgeForFemaleSpawnerForFirstTime); + } - @Observable(description="Higher Populated Latitude") - public double getHigherPopulatedLatitude() { - double latitude = 0.0; - RiverBasin[] basins = getEnvironment().getRiverBasins(); - int[] finalStates = getEnvironment().getFinalStates(); - for (int i = 0; i < finalStates.length; i++) { - if ((finalStates[i] == 1) && (basins[i].getLatitude() > latitude)) { - latitude = basins[i].getLatitude(); - } + @Observable(description="mean age at first reproduction for female") + public double getMeanAgeOfFirstReprodutionForFemale() { + double sum = 0; + double nb =0; + for (RiverBasin riverBasin : getEnvironment().getRiverBasins()) { + if (riverBasin.getSpawnersForFirstTimeMeanAges(Gender.FEMALE).getMeanWithoutZero() > 0.) { + nb ++; + sum += riverBasin.getSpawnersForFirstTimeMeanAges(Gender.FEMALE).getMeanWithoutZero() ; + } } - return latitude; + return sum/nb; } - @Observable(description="Spawners For First Time Summary Statistic") - public double computeSpawnerForFirstTimeSummaryStatistic() { + + @Observable(description="mean length of first reprodution for female") + public double getMeanLengthOfFirstReprodutionForFemale() { double sum = 0; - double TARGET = 5.0; + double nb =0; for (RiverBasin riverBasin : getEnvironment().getRiverBasins()) { - if (riverBasin.getSpawnersForFirstTimeMeanAges().getMeanWithoutZero() > 0.) - sum += Math.pow(riverBasin.getSpawnersForFirstTimeMeanAges().getMeanWithoutZero() - TARGET, 2); + if (riverBasin.getSpawnersForFirstTimeMeanLengths(Gender.FEMALE).getMeanWithoutZero() > 0.) { + nb ++; + sum += riverBasin.getSpawnersForFirstTimeMeanLengths(Gender.FEMALE).getMeanWithoutZero() ; + } } + return sum/nb; + } + + + public double computeMaleSpawnerForFirstTimeSummaryStatisticWithTarget(double TARGET ) { + double sum = 0; + for (RiverBasin riverBasin : getEnvironment().getRiverBasins()) { + if (riverBasin.getSpawnersForFirstTimeMeanAges(Gender.MALE).getMeanWithoutZero() > 0.) { + double val = riverBasin.getSpawnersForFirstTimeMeanAges(Gender.MALE).getMeanWithoutZero() - TARGET; + sum += val * val; + } + } + //System.out.println("sum male: " + sum); return sum; } - - @Observable(description = "Likelihood Summary stat") - public double computeLikelihood() throws IOException { - // 1 : read input file of observation - FileReader reader; - Scanner scanner; - Map<String, Integer> obs1900 = new HashMap<String, Integer>(); - try { - reader = new FileReader(fileNameInputForInitialObservation); - // Parsing the file - scanner = new Scanner(reader); - scanner.useLocale(Locale.ENGLISH); // to have a comma as decimal separator !!! - scanner.useDelimiter(Pattern.compile("[;\r]")); - - scanner.nextLine(); // to skip the file first line of entete - - while (scanner.hasNext()) { - obs1900.put(scanner.next().replaceAll("\n", ""), scanner.nextInt()); + @Observable(description="Male spawners For First Time Summary Statistic") + public double computeMaleSpawnerForFirstTimeSummaryStatistic() { + return computeMaleSpawnerForFirstTimeSummaryStatisticWithTarget(targetedAgeForFemaleSpawnerForFirstTime); + } + + @Observable(description="mean age of first reprodution for male") + public double getMeanAgeOfFirstReprodutionForMale() { + double sum = 0; + double nb =0; + for (RiverBasin riverBasin : getEnvironment().getRiverBasins()) { + if (riverBasin.getSpawnersForFirstTimeMeanAges(Gender.MALE).getMeanWithoutZero() > 0.) { + nb ++; + sum += riverBasin.getSpawnersForFirstTimeMeanAges(Gender.MALE).getMeanWithoutZero() ; } - reader.close(); - scanner.close(); - - } catch (IOException ex) { - Logger.getLogger(DiadromousFishGroup.class.getName()).log(Level.SEVERE, null, ex); } + return sum/nb; + } + @Observable(description="mean length of first reprodution for male") + public double getMeanLengthOfFirstReprodutionForMale() { + double sum = 0; + double nb =0; + for (RiverBasin riverBasin : getEnvironment().getRiverBasins()) { + if (riverBasin.getSpawnersForFirstTimeMeanLengths(Gender.MALE).getMeanWithoutZero() > 0.) { + nb ++; + sum += riverBasin.getSpawnersForFirstTimeMeanLengths(Gender.MALE).getMeanWithoutZero() ; + } + } + return sum/nb; + } + + + @Observable(description = "Likelihood Summary stat") + public double computeLikelihood() { int obsVal; double sumLogWherePres = 0.; double sumLogWhereAbs = 0.; @@ -338,6 +775,25 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe return sumLogWhereAbs + sumLogWherePres; } + + // ======================================================== + // obeserver to explore the distribution + // ======================================================== + @Observable(description="Higher Populated Latitude") + public double getHigherPopulatedLatitude() { + double latitude = 0.0; + RiverBasin[] basins = getEnvironment().getRiverBasins(); + int[] finalStates = getEnvironment().getFinalStates(); + for (int i = 0; i < finalStates.length; i++) { + if ((finalStates[i] == 1) && (basins[i].getLatitude() > latitude)) { + latitude = basins[i].getLatitude(); + } + + } + return latitude; + } + + @Observable(description="Number of colonized basins") public double getNbColonizedBasins() { int nb = 0; @@ -351,6 +807,7 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe return nb; } + @Observable(description="Northern colonized basins") public double getNorthernBasins() { int northernBasin = Integer.MAX_VALUE; @@ -364,6 +821,7 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe return northernBasin; } + @Observable(description="Southern colonized basins") public double getSouthernBasins() { int southernBasin = Integer.MIN_VALUE; @@ -408,6 +866,7 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe return rangeDistribution; } + @Observable(description = "Range distribution") public Double[] getRangeDistribution() { double southernBasin = 0; @@ -440,15 +899,23 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe return rangeDistribution; } - @Observable(description = "Number of fishes") + /** + * @return sum of effectives in all the river basins + */ + @Observable(description = "Number of fishes in river basin") public double getFishEffective() { long eff = 0; - for (DiadromousFish fish : this.getAquaNismsList()) { - eff += fish.getAmount(); + for (RiverBasin basin : this.getEnvironment().getRiverBasins()){ + if (basin.getFishs(this) != null) { + for (DiadromousFish fish : basin.getFishs(this)) { + eff += fish.getAmount(); + } + } } return eff; } + @Override public void addAquaNism(DiadromousFish fish) { // avoid utilisation of global fishes list @@ -456,6 +923,7 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe fish.getPosition().addFish(fish, this); } + @Override public void removeAquaNism(DiadromousFish fish) { // avoid utilisation of global fishes list @@ -463,11 +931,18 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe fish.getPosition().removeFish(fish, this); } + @Override public int compareTo(DiadromousFishGroup t) { return name.compareTo(t.name); } + + /** + * + * concat at RngSatusIndex, temperatureCatchmentFile + * @return simulation name + */ public String getSimulationId(){ String id="_"; id=id.concat(Integer.toString(getPilot().getParameters().getRngStatusIndex())); @@ -482,7 +957,7 @@ public class DiadromousFishGroup extends AquaNismsGroup< DiadromousFish, BasinNe public boolean isThereBasinToUpdate(){ return basinsToUpdate != null; } - + /** * @return the outputPath */ diff --git a/src/main/java/species/DisperseAndMigrateToRiver.java b/src/main/java/species/DisperseAndMigrateToRiver.java index fe7c1ff16e59e0c5f5084cfac8c1042f5137d548..db6d18486d126b61478b3dad6907c14b887d8d34 100644 --- a/src/main/java/species/DisperseAndMigrateToRiver.java +++ b/src/main/java/species/DisperseAndMigrateToRiver.java @@ -49,26 +49,26 @@ public class DisperseAndMigrateToRiver extends DisperseAndMigrateToRiverBasic { if (fish.isMature()) { // fish with homing - amountWithHoming = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), pHoming); // seuil par défaut fixé à 50 + amountWithHoming = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), pHoming); // seuil par d�faut fix� � 50 // strayed fish strayedAmount = fish.getAmount() - amountWithHoming; if (strayedAmount != 0) { - // On récupère les info du poids des bassin par rapport à la position du poisson + // On r�cup�re les info du poids des bassin par rapport � la position du poisson Map<Basin,Double> accBasOfFish= new TreeMap<Basin, Double>(accessibleBasinsPerBasin.get(fish.getPosition())); //accBasOfFish = accessibleBasinsPerBasin.get(fish.getPosition()); - // On retire certains bassins si on considère une distance max de dispersion + // On retire certains bassins si on consid�re une distance max de dispersion distBasOfFish = distanceBasinsPerBasin.get(fish.getPosition()); if (group.getdMaxDisp() != 0){ - // TODO pourquoi distbasoffish peut être nul ? + // TODO pourquoi distbasoffish peut �tre nul ? if (distBasOfFish != null){ - dMaxDispFish = (group.getdMaxDisp()/group.getLinfVonBert())*fish.getLength(); + dMaxDispFish = (group.getdMaxDisp()/group.getLinfVonBert(fish))*fish.getLength(); // load accessible basins for (Basin surroundingBasin : distBasOfFish.keySet()){ Double distance = distBasOfFish.get(surroundingBasin); - //System.out.println("pour le poisson " + fish.hashCode() + " situé dans le bassin " + basin.getName() + " et né dans le bassin " + fish.getBirthBasin().getName()); + //System.out.println("pour le poisson " + fish.hashCode() + " situ� dans le bassin " + basin.getName() + " et n� dans le bassin " + fish.getBirthBasin().getName()); //System.out.println("la distance vaut " + distance + " pour le bassin " + surroundingBasin.getName()); if (distance >= dMaxDispFish) { accBasOfFish.remove(surroundingBasin); @@ -81,7 +81,7 @@ public class DisperseAndMigrateToRiver extends DisperseAndMigrateToRiverBasic { double totalWeight = 0.; double probToGo = 0.; long amountToGo = 0; - // TODO Qu'est ce qui se passe si AccBasOfFish est vide... ça beug pas mais c'est pas très clair... donc à vérifier + // TODO Qu'est ce qui se passe si AccBasOfFish est vide... �a beug pas mais c'est pas tr�s clair... donc � v�rifier for (Basin accBasin : accBasOfFish.keySet()){ double accBasinWeightLogit = accBasOfFish.get(accBasin) + alpha2Rep * fish.getLength(); double accBasinWeight = 1 / (1 + Math.exp(- accBasinWeightLogit)); @@ -107,7 +107,7 @@ public class DisperseAndMigrateToRiver extends DisperseAndMigrateToRiverBasic { if (amountWithHoming > 0){ fish.setAmount(amountWithHoming); // retour soit dans le bassin de naissance pour les semelpares - // soit dans le dernier bassin de reproduction pour les itéropares + // soit dans le dernier bassin de reproduction pour les it�ropares fish.moveTo(group.getPilot(), bn.getAssociatedRiverBasin(fish.getPosition()), group); } else { deadFish.add(fish); diff --git a/src/main/java/species/DisperseAndMigrateToRiverBasic.java b/src/main/java/species/DisperseAndMigrateToRiverBasic.java index 37b2d0b1013d5beda0153d410c2163e4ce0d220f..cbbae2950cdd2230ec04568b2fe1d6369d450445 100644 --- a/src/main/java/species/DisperseAndMigrateToRiverBasic.java +++ b/src/main/java/species/DisperseAndMigrateToRiverBasic.java @@ -21,31 +21,86 @@ import com.thoughtworks.xstream.io.xml.DomDriver; @ServiceProvider(service = AquaNismsGroupProcess.class) public class DisperseAndMigrateToRiverBasic extends AquaNismsGroupProcess<DiadromousFish, DiadromousFishGroup> { + /** the coefficient independent of environmental factors in the logistic function used to calculate the probability to disperse + * @unit - + */ private double alpha0Rep = -2.2; - private double alpha1Rep = 17.3; + + /** + * the coefficient associated with the distance between catchment in the logistic function used to calculate the probability to disperse + * i.e. the relative influence of accessibility + * @unit - + */ + //TODO transform to a negative value (the larger the distance , the smaller the accessibility is) and correct in the computation of the weight + private double alpha1Rep = 17.3; + + /** + * the mean distance between catchments used to standardize the inter-catchment distance in the logistic function that calculates the probability to disperse + * @unit km + */ + private double meanInterDistance = 300.; // (from the 53 cathments among the 173 of Lassalles 2008) + + /** + * the standard deviation of distances between catchments used to standardize the inter-catchment distance in the logistic function that calculates the probability to disperse + * @unit km + */ + private double standardDeviationInterDistance = 978.; // (from the 53 cathments among the 173 of Lassalles 2008) + + /** + * the coefficient associated with the attractive surface of the catchment in the logistic function used to calculate the probability to disperse + * i.e. the relative influence of attractiveness + * should be positive : the larger the surface , the higher the attractiveness is + * @unit - + */ + //TODO check the sign in the formula private double alpha3Rep = 0.; - private double meanBvSurface = 23071., standardDeviationBvSurface = 39833.; // for standard core values... Value for the selected 54 BV of the Atlantic Coast from the 196 BV of LAssale, 2008 - private double meanInterDistance = 300., standardDeviationInterDistance = 978.; // for standard core values...Value for the selected 54 BV of the Atlantic Coast from the 196 BV of LAssale, 2008 + /** + * the mean surface used to standardize the catchment surface in the logistic function that calculates the probability to disperse + * @unit ? ha ? + */ + private double meanBvSurface = 23071.; // (from the 53 cathments among the 173 of Lassalles 2008) + + /** + * the standard deviation used to standardize the catchment surface in the logistic function that calculates the probability to disperse + * @unit ? ha ? + */ + private double standardDeviationBvSurface = 39833.; // (from the 53 cathments among the 173 of Lassalles 2008) + /** + * a map associtaing a sea bassin with the weight (accessibility and atrrtactivity) for each river bassin + * <key> SeaBasin + * <value> + * <key> RiverBasin + * <value> weight to calculate probaility to disperse + */ protected transient Map<Basin,Map<Basin,Double>> accessibleBasinsPerBasin; + + /** + * a map associtaing a sea bassin with the distance for each river bassin + * <key> SeaBasin + * <value> + * <key> RiverBasin + * <value> distance between the river Basin and the river basin associated with the sea basin + */ protected transient Map<Basin,Map<Basin,Double>> distanceBasinsPerBasin; - //private transient ObservablesHandler cObservable; + @Override @InitTransientParameters public void initTransientParameters(Pilot pilot) { super.initTransientParameters(pilot); - // calcul les poids des bassins voisins qui ne dépendent pas des poissons pour chaque SeaBassin + // calcul les poids des bassins voisins qui ne d�pendent pas des poissons pour chaque SeaBassin BasinNetwork bn = (BasinNetwork) pilot.getAquaticWorld().getEnvironment(); accessibleBasinsPerBasin = new TreeMap<Basin, Map<Basin,Double>>(); distanceBasinsPerBasin = new TreeMap<Basin, Map<Basin,Double>>(); for (Basin seaBas : bn.getSeaBasins()){ + // compoute the distance with between seaBas and all the river basins Map<Basin,Double> mapDist = bn.getNeighboursWithDistance(seaBas); distanceBasinsPerBasin.put(seaBas, mapDist); - // Compute the weight of each basin + // Compute the weight of each river basin Map<Basin,Double> accessibleBasins = bn.getNeighboursWithDistance(seaBas); for (Basin bas : accessibleBasins.keySet()){ double weight = alpha0Rep diff --git a/src/main/java/species/DisperseAndMigrateToRiverStandardization.java b/src/main/java/species/DisperseAndMigrateToRiverStandardization.java index 9aa835719d0adc1d972d5d3f79d8c08d3aff423c..d810e41858395c42f0b344e4200222908e12b8f6 100644 --- a/src/main/java/species/DisperseAndMigrateToRiverStandardization.java +++ b/src/main/java/species/DisperseAndMigrateToRiverStandardization.java @@ -41,7 +41,7 @@ public class DisperseAndMigrateToRiverStandardization extends AquaNismsGroupProc public void initTransientParameters(Pilot pilot) { super.initTransientParameters(pilot); - // calcul les poids des bassins voisins qui ne dépendent pas des poissons pour chaque SeaBassin + // calcul les poids des bassins voisins qui ne d�pendent pas des poissons pour chaque SeaBassin BasinNetwork bn = (BasinNetwork) pilot.getAquaticWorld().getEnvironment(); accessibleBasinsPerBasin = new TreeMap<Basin, Map<Basin,Double>>(); distanceBasinsPerBasin = new TreeMap<Basin, Map<Basin,Double>>(); @@ -107,26 +107,26 @@ public class DisperseAndMigrateToRiverStandardization extends AquaNismsGroupProc if (fish.isMature()) { // fish with homing - amountWithHoming = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), pHoming); // seuil par défaut fixé à 50 + amountWithHoming = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), pHoming); // seuil par d�faut fix� � 50 // strayed fish strayedAmount = fish.getAmount() - amountWithHoming; if (strayedAmount != 0) { - // On récupère les info du poids des bassin par rapport à la position du poisson + // On r�cup�re les info du poids des bassin par rapport � la position du poisson Map<Basin,Double> accBasOfFish= new TreeMap<Basin, Double>(accessibleBasinsPerBasin.get(fish.getPosition())); //accBasOfFish = accessibleBasinsPerBasin.get(fish.getPosition()); - // On retire certains bassins si on considère une distance max de dispersion + // On retire certains bassins si on consid�re une distance max de dispersion distBasOfFish = distanceBasinsPerBasin.get(fish.getPosition()); if (group.getdMaxDisp() != 0){ - // TODO pourquoi distbasoffish peut être nul ? + // TODO pourquoi distbasoffish peut �tre nul ? if (distBasOfFish != null){ - dMaxDispFish = (group.getdMaxDisp()/group.getLinfVonBert())*fish.getLength(); + dMaxDispFish = (group.getdMaxDisp()/group.getLinfVonBert(fish))*fish.getLength(); // load accessible basins for (Basin surroundingBasin : distBasOfFish.keySet()){ Double distance = distBasOfFish.get(surroundingBasin); - //System.out.println("pour le poisson " + fish.hashCode() + " situé dans le bassin " + basin.getName() + " et né dans le bassin " + fish.getBirthBasin().getName()); + //System.out.println("pour le poisson " + fish.hashCode() + " situ� dans le bassin " + basin.getName() + " et n� dans le bassin " + fish.getBirthBasin().getName()); //System.out.println("la distance vaut " + distance + " pour le bassin " + surroundingBasin.getName()); if (distance >= dMaxDispFish) { accBasOfFish.remove(surroundingBasin); @@ -139,7 +139,7 @@ public class DisperseAndMigrateToRiverStandardization extends AquaNismsGroupProc double totalWeight = 0.; double probToGo = 0.; long amountToGo = 0; - // TODO Qu'est ce qui se passe si AccBasOfFish est vide... ça beug pas mais c'est pas très clair... donc à vérifier + // TODO Qu'est ce qui se passe si AccBasOfFish est vide... �a beug pas mais c'est pas tr�s clair... donc � v�rifier for (Basin accBasin : accBasOfFish.keySet()){ double accBasinWeightLogit = accBasOfFish.get(accBasin) + alpha2Rep*((fish.getLength() - meanLengthOfMatureFishes) / standardDeviationOfMatureFishesLength); double accBasinWeight = 1 / (1 + Math.exp(- accBasinWeightLogit)); @@ -165,7 +165,7 @@ public class DisperseAndMigrateToRiverStandardization extends AquaNismsGroupProc if (amountWithHoming > 0){ fish.setAmount(amountWithHoming); // retour soit dans le bassin de naissance pour les semelpares - // soit dans le dernier bassin de reproduction pour les itéropares + // soit dans le dernier bassin de reproduction pour les it�ropares fish.moveTo(group.getPilot(), bn.getAssociatedRiverBasin(fish.getPosition()), group); } else { deadFish.add(fish); diff --git a/src/main/java/species/DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin.java b/src/main/java/species/DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin.java index b7f86e6c9a66d06a0c1100257a2d95c4506a0daa..e3949b2d192896b996f5c293eafef5cc4903af9f 100644 --- a/src/main/java/species/DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin.java +++ b/src/main/java/species/DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin.java @@ -18,17 +18,71 @@ import com.thoughtworks.xstream.io.xml.DomDriver; import miscellaneous.Duo; +/** + * + */ @ServiceProvider(service = AquaNismsGroupProcess.class) public class DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin extends DisperseAndMigrateToRiverBasic { + + /** + * the season when fish migrate to the river to reproduce + * @unit + */ + private Season riverMigrationSeason = Season.SPRING; + + /** + * the homing probalilty during the installation of new populations ( to reach kind of equilibrium) + * @unit + */ private double pHomingForReachEquil = 1.0; + + /** + * the homing probalilty after the installation of new populations ( after reaching an equilibrium) + * @unit + */ private double pHomingAfterEquil = 0.8; + + /** + * Number of year for newly created populations to be installed ( to reach an equilibrium) + * @unit + */ private long NbYearForInstallPop = 50; - private Season riverMigrationSeason = Season.SPRING; + + + /** the coefficient associated with the fish size in the logistic function used to calculate the probability to disperse + * @unit - + */ private double alpha2Rep = 0.; - private double meanSpawnersLengthAtRepro = 45., standardDeviationOfSpawnersLengthAtRepro = 2.; // for standard core values... + + /** + * the mean length used to standardize the fish length in the logistic function that calculates the probability to disperse + * @unit - + */ + private double meanSpawnersLengthAtRepro = 45.; + + /** + * the length standard deviation used to standardize the fish length in the logistic function that calculates the probability to disperse + * @unit - + */ + private double standardDeviationOfSpawnersLengthAtRepro = 2.; // for standard core values... + + /** + * the weigth of the death bassin ( for strayers that do not find a catcment) used to calculate the probability to disperse + * @unit + */ private double weightOfDeathBasin = 0.2; + + /** + * a bollean to kill of the strayers (used to determine if a catchment is a souce or a sink) the year given by yearOfTheKilling + * @unit + */ private boolean killStrayers; + + /** + * the year when the strayers are killed (used to determine if a catchment is a souce or a sink) if killStrayers is true + * @unit + */ private long yearOfTheKillings; public static void main(String[] args) { @@ -43,8 +97,9 @@ public class DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin extends Di BasinNetwork bn = group.getEnvironment(); long amountWithHoming, strayedAmount; - double pHoming; + // probability of homing + double pHoming; if (Time.getYear(group.getPilot()) < NbYearForInstallPop) { pHoming = pHomingForReachEquil; } else { @@ -54,18 +109,23 @@ public class DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin extends Di List<DiadromousFish> deadFish = new ArrayList<DiadromousFish>(); List<DiadromousFish> newFish = new ArrayList<DiadromousFish>(); + // creation of the death basin (for the lost strayers) + //TODO move as a transient field Basin deathBasin = new Basin(-1, "deathBasin", 0, 0, 0, 0); + List<Duo<DiadromousFish, Basin>> fishesToMove = new ArrayList<Duo<DiadromousFish, Basin>>(); for (Basin basin : group.getEnvironment().getSeaBasins()) { List<DiadromousFish> fishes = basin.getFishs(group); if (fishes != null) { - for (int j = 0; j < fishes.size(); j++) { - DiadromousFish fish = fishes.get(j); + for (DiadromousFish fish : fishes) { + + // verify that fish is in a sea basin assert fish.getPosition().getType() == Basin.TypeBassin.SEA; + if (fish.isMature()) { // fish with homing - amountWithHoming = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), pHoming); // seuil par défaut fixé à 50 + amountWithHoming = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), pHoming); // seuil par d�faut fix� � 50 // strayed fish if (killStrayers == true && Time.getYear(group.getPilot()) >= yearOfTheKillings) { @@ -75,10 +135,12 @@ public class DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin extends Di strayedAmount = fish.getAmount() - amountWithHoming; } + // influence of the fish length on the probability to disperse if (strayedAmount != 0) { - double weightFishLength = Math.exp(-(alpha2Rep * ((fish.getLength() - meanSpawnersLengthAtRepro) / standardDeviationOfSpawnersLengthAtRepro))); + // calcula the weight associated with the fish length in the probabaility to disperse + double weightFishLength = -(alpha2Rep * ((fish.getLength() - meanSpawnersLengthAtRepro) / standardDeviationOfSpawnersLengthAtRepro)); - // On récupère les info du poids des bassin par rapport à la position du poisson + // upload the weights associated with features of the catchment (accessibility and attractivity) List<Duo<Basin, Double>> accBasOfFish = new ArrayList<Duo<Basin, Double>>(); for (Map.Entry<Basin, Double> entry : accessibleBasinsPerBasin.get(fish.getPosition()).entrySet()) { Duo<Basin, Double> duo = new Duo<Basin, Double>(entry.getKey(), entry.getValue()); @@ -89,11 +151,13 @@ public class DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin extends Di double totalWeight = 0.; double probToGo = 0.; long amountToGo = 0; - // TODO Qu'est ce qui se passe si AccBasOfFish est vide... ça beug pas mais c'est pas très clair... donc à vérifier + // TODO manage the case when AccBasOfFish is empty for (Duo<Basin, Double> accBasin : accBasOfFish) { + // total weight for the basin Basin b = accBasin.getFirst(); Double weight = accBasin.getSecond(); - double accBasinWeight = 1 / (1 + Math.exp(-weight) * weightFishLength); + double accBasinWeight = 1 / (1 + Math.exp(-(weight + weightFishLength))); + // put weight to 0 for unused basins if (group.isThereBasinToUpdate()){ if (Time.getYear(group.getPilot()) >= group.getYearOfTheUpdate() @@ -104,10 +168,8 @@ public class DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin extends Di } accBasin.setSecond(accBasinWeight); totalWeight += accBasinWeight; - //} } - // add the deathBasin in the list accBasOfFish.add(new Duo<Basin, Double>(deathBasin, weightOfDeathBasin)); totalWeight = totalWeight + weightOfDeathBasin; @@ -141,7 +203,7 @@ public class DisperseAndMigrateToRiverWithMultiNomDistriAndDeathBasin extends Di if (amountWithHoming > 0) { fish.setAmount(amountWithHoming); // retour soit dans le bassin de naissance pour les semelpares - // soit dans le dernier bassin de reproduction pour les itéropares + // soit dans le dernier bassin de reproduction pour les it�ropares fishesToMove.add(new Duo<DiadromousFish, Basin>(fish, bn.getAssociatedRiverBasin(fish.getPosition()))); } else { deadFish.add(fish); diff --git a/src/main/java/species/ExportBiomass.java b/src/main/java/species/ExportBiomass.java new file mode 100644 index 0000000000000000000000000000000000000000..e4899f82a0ae08f9e8ae64eb5709b42626f88627 --- /dev/null +++ b/src/main/java/species/ExportBiomass.java @@ -0,0 +1,120 @@ +package species; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.DomDriver; + +import environment.RiverBasin; +import environment.Time; +import environment.Time.Season; +import fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess; +import fr.cemagref.simaqualife.pilot.Pilot; +import miscellaneous.Miscellaneous; +import species.DiadromousFish.Stage; +import species.ReproduceAndSurviveAfterReproductionWithDiagnose; + +/** + * + */ +public class ExportBiomass extends AquaNismsGroupProcess<DiadromousFish, DiadromousFishGroup> { + + private double survivalRateAfterReproduction = 0.1; + private Season exportSeason = Season.SPRING; + + private String fileNameOutput = "BiomassFluxes"; + + private transient BufferedWriter bW; + private transient String sep=";"; + + + public static void main(String[] args) { + System.out.println((new XStream(new DomDriver())) + .toXML(new ExportBiomass())); + } + + /* (non-Javadoc) + * @see fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess#initTransientParameters(fr.cemagref.simaqualife.pilot.Pilot) + */ + @Override + public void initTransientParameters(Pilot pilot) { + super.initTransientParameters(pilot); + sep=";"; + + } + + @Override + public void doProcess(DiadromousFishGroup group) { + + if (bW==null){ + if (fileNameOutput != null){ + new File(group.getOutputPath()+fileNameOutput).getParentFile().mkdirs(); + try { + bW = new BufferedWriter(new FileWriter(new File(group.getOutputPath()+ + fileNameOutput +group.getSimulationId()+ ".csv"))); + + bW.write("year"+sep+"migrationBasin" ); //create the field of the column + for (String birthBasinName : group.getEnvironment().getRiverBasinNames()) { + bW.write(sep + birthBasinName); // write each basin name in the file + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + try { + if (Time.getSeason(pilot) == exportSeason & Time.getYear(pilot)>1900) { + + + for (RiverBasin migrationBasin : group.getEnvironment().getRiverBasins()) { + //Create the map to get the biomass in each birth basin + Map<String, Double> spawnerOriginsBeforeReproduction = new HashMap<String, Double>(group.getEnvironment().getRiverBasinNames().length); + for (String basinName : group.getEnvironment().getRiverBasinNames()){ + spawnerOriginsBeforeReproduction.put(basinName, 0.); + } + double biomass=0.; + //compute the cumulative effective per birth basin + if (migrationBasin.getFishs(group) != null) { + + for (DiadromousFish fish : migrationBasin.getFishs(group)) { + + double survivalAmount = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), survivalRateAfterReproduction); + biomass = group.getNutrientRoutine().getWeight(fish) * (fish.getAmount() - survivalAmount); + + if (fish.getStage() == Stage.MATURE) { + String birthBasinName = fish.getBirthBasin().getName(); + spawnerOriginsBeforeReproduction.put(birthBasinName, spawnerOriginsBeforeReproduction.get(birthBasinName) + biomass); + + + } + + } + } + + //write the first two fields of the line + bW.write(Time.getYear(pilot)+sep+migrationBasin.getName()); + + //write the cumulative effective from birth basin + for (String birthBasinName : group.getEnvironment().getRiverBasinNames()) { + bW.write(sep+spawnerOriginsBeforeReproduction.get(birthBasinName)); + } + //write an end-of(line + bW.write("\n"); + } + } + if (group.getPilot().getCurrentTime()== group.getPilot().getSimBegin()+group.getPilot().getSimDuration()-1) + bW.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/species/ExportFluxes.java b/src/main/java/species/ExportFluxes.java new file mode 100644 index 0000000000000000000000000000000000000000..a258ba0c0775d044b248dca5979c316d4846e18a --- /dev/null +++ b/src/main/java/species/ExportFluxes.java @@ -0,0 +1,126 @@ +/** + * patrick.lambert + * @author Patrick Lambert + * @copyright Copyright (c) 2018, Irstea + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ +package species; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.DomDriver; + +import environment.RiverBasin; +import environment.Time; +import environment.Time.Season; +import fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess; +import fr.cemagref.simaqualife.pilot.Pilot; +import species.DiadromousFish.Stage; + +/** + * + */ +public class ExportFluxes extends AquaNismsGroupProcess<DiadromousFish, DiadromousFishGroup> { + + private Season exportSeason = Season.SPRING; + + private String fileNameOutput = "effectiveFluxes"; + + private transient BufferedWriter bW; + private transient String sep=";"; + + public static void main(String[] args) { + System.out.println((new XStream(new DomDriver())) + .toXML(new ExportFluxes())); + } + + /* (non-Javadoc) + * @see fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess#initTransientParameters(fr.cemagref.simaqualife.pilot.Pilot) + */ + @Override + public void initTransientParameters(Pilot pilot) { + super.initTransientParameters(pilot); + sep=";"; + + } + + @Override + public void doProcess(DiadromousFishGroup group) { + + if (bW==null){ + if (fileNameOutput != null){ + new File(group.getOutputPath()+fileNameOutput).getParentFile().mkdirs(); + try { + bW = new BufferedWriter(new FileWriter(new File(group.getOutputPath()+ + fileNameOutput +group.getSimulationId()+ ".csv"))); + + bW.write("year"+sep+"migrationBasin" ); //create the field of the column + for (String birthBasinName : group.getEnvironment().getRiverBasinNames()) { + bW.write(sep + birthBasinName); // write each basin name in the file + } + bW.write("\n"); + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + try { + if (Time.getSeason(pilot) == exportSeason & Time.getYear(pilot)>1900) { + + + for (RiverBasin migrationBasin : group.getEnvironment().getRiverBasins()) { + //Create the map to get the abundance in each birth basin + Map<String, Long> spawnerOriginsBeforeReproduction = new HashMap<String, Long>(group.getEnvironment().getRiverBasinNames().length); + for (String basinName : group.getEnvironment().getRiverBasinNames()){ + spawnerOriginsBeforeReproduction.put(basinName, 0L); + } + + //compute the cumulative effective per birth basin + if (migrationBasin.getFishs(group) != null) { + for (DiadromousFish fish : migrationBasin.getFishs(group)) { + if (fish.getStage() == Stage.MATURE) { + String birthBasinName = fish.getBirthBasin().getName(); + spawnerOriginsBeforeReproduction.put(birthBasinName, spawnerOriginsBeforeReproduction.get(birthBasinName) + fish.getAmount() ); + } + } + } + + //write the first two fields of the line + bW.write(Time.getYear(pilot)+sep+migrationBasin.getName()); + + //write the cumulative effective from birth basin + for (String birthBasinName : group.getEnvironment().getRiverBasinNames()) { + bW.write(sep+spawnerOriginsBeforeReproduction.get(birthBasinName)); + } + // write an end-of-line + bW.write("\n"); + } + } + if (group.getPilot().getCurrentTime()== group.getPilot().getSimBegin()+group.getPilot().getSimDuration()-1) + bW.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/species/ExportLenghtAgeDistribution.java b/src/main/java/species/ExportLenghtAgeDistribution.java new file mode 100644 index 0000000000000000000000000000000000000000..fbe679136873cecb7eee7d1cdd191a58cc273cf6 --- /dev/null +++ b/src/main/java/species/ExportLenghtAgeDistribution.java @@ -0,0 +1,113 @@ +/** + * patrick.lambert + * @author Patrick Lambert + * @copyright Copyright (c) 2018, Irstea + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ +package species; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.DomDriver; + +import environment.RiverBasin; +import environment.Time; +import environment.Time.Season; +import fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess; +import fr.cemagref.simaqualife.pilot.Pilot; +import species.DiadromousFish.Stage; + +/** + * + */ +public class ExportLenghtAgeDistribution extends AquaNismsGroupProcess<DiadromousFish, DiadromousFishGroup> { + + private Season exportSeason = Season.SPRING; + + private String fileNameOutput = "lengthAgeDistribution"; + + private transient BufferedWriter bW; + private transient String sep=";"; + + public static void main(String[] args) { + System.out.println((new XStream(new DomDriver())) + .toXML(new ExportLenghtAgeDistribution())); + } + + /* (non-Javadoc) + * @see fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess#initTransientParameters(fr.cemagref.simaqualife.pilot.Pilot) + */ + @Override + public void initTransientParameters(Pilot pilot) { + super.initTransientParameters(pilot); + sep=";"; + + } + + @Override + public void doProcess(DiadromousFishGroup group) { + + if (bW==null){ + if (fileNameOutput != null){ + new File(group.getOutputPath()+fileNameOutput).getParentFile().mkdirs(); + try { + bW = new BufferedWriter(new FileWriter(new File(group.getOutputPath()+ + fileNameOutput +group.getSimulationId()+ ".csv"))); + + bW.write("timestep"+sep+"year"+sep+"season"+sep+"basin"+ sep+"gender" + +sep+ "effective" +sep+"length"+sep+"age"+sep+"nbSpawn"+"\n"); + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + try { + if (Time.getSeason(pilot) == exportSeason & Time.getYear(pilot)>1900) { + for (RiverBasin basin : group.getEnvironment().getRiverBasins()) { + if (basin.getFishs(group) != null) { + for (DiadromousFish fish : basin.getFishs(group)) { + if (fish.getStage() == Stage.MATURE) { + + //System.out.println(fish.getAge() + " -> "+ fish.getLength() +" - "+fish.getNumberOfReproduction()); + + bW.write(pilot.getCurrentTime() + sep); + bW.write(Time.getYear(pilot) + sep); + bW.write(Time.getSeason(pilot) + sep); + bW.write(basin.getName() + sep); + bW.write(fish.getGender() + sep); + bW.write(fish.getAmount() + sep); + bW.write(fish.getLength() + sep); + bW.write(fish.getAge()+ sep); + bW.write(fish.getNumberOfReproduction()+ "\n"); + } + } + } + } + } + if (group.getPilot().getCurrentTime()== group.getPilot().getSimBegin()+group.getPilot().getSimDuration()-1) + bW.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} + diff --git a/src/main/java/species/FishNutrient.java b/src/main/java/species/FishNutrient.java deleted file mode 100644 index 23b7d94604254e0872044841b602a009d63d0325..0000000000000000000000000000000000000000 --- a/src/main/java/species/FishNutrient.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * patrick.lambert - * @author Patrick Lambert - * @copyright Copyright (c) 2018, Irstea - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - - */ -package species; - -import species.DiadromousFish.Gender; -import species.DiadromousFish.Stage; - -/** - * - */ -public class FishNutrient { - - private double aLWfemalePre = 0.0221; // param�tre "a" de la relation taille/poids avec Lt en cm - private double bLWfemalePre = 2.8147; // param�tre "b" de la relation taille/poids - private double GSIfemalePre =.15; - private double aLWmalePre = 0.0221; // param�tre "a" de la relation taille/poids avec Lt en cm - private double bLWmalePre = 2.8147; // param�tre "b" de la relation taille/poids - private double GSImalePre =.07; - - // Si on ne possède pas wT post reproduction - - private double aLWfemalePost = 0.; // param�tre "a" de la relation taille/poids avec Lt en cm - private double bLWfemalePost = 0.; // param�tre "b" de la relation taille/poids - private double GSIfemalePost=0.; - private double aLWmalePost = 0.; // param�tre "a" de la relation taille/poids avec Lt en cm - private double bLWmalePost = 0.; // param�tre "b" de la relation taille/poids - private double GSImalePost =.07; - - - - //Valeurs de Haskell pour A. sapidissima -- A rechercher pour Alosa alosa - - private double compoNpreMale = 2.921; - private double compoPpreMale = 0.662; - private double compoNpreFemale = 2.917; - private double compoPpreFemale = 0.725; - - private double compoNpostMale = 2.790 ; - private double compoPpostMale = 0.961; - private double compoNpostFemale = 3.216 ; - private double compoPpostFemale = 0.997; - - - /** - * - */ - public FishNutrient() { - // TODO Auto-generated constructor stub - } - - public void computeNP(DiadromousFish fish) { - double totalWeightPre, totalWeightPost; - - if (fish.getStage() == Stage.MATURE) { - if (fish.getGender() == Gender.FEMALE ) { - totalWeightPre = aLWfemalePre * Math.pow(fish.getLength(), bLWfemalePre); - totalWeightPost = aLWfemalePost * Math.pow(fish.getLength(), bLWfemalePost); - // totalWeightPost = totalWeightPre * GSIfemalePre; - } - else if (fish.getGender() == Gender.MALE) { - totalWeightPre = aLWmalePre * Math.pow(fish.getLength(), bLWmalePre); - totalWeightPost = aLWmalePost * Math.pow(fish.getLength(), bLWmalePost); - // totalWeightPost = totalWeightPre * GSImalePre; - } - else { - totalWeightPre = Double.NaN; - totalWeightPost = 0.; - } - - } - } -} \ No newline at end of file diff --git a/src/main/java/species/Grow.java b/src/main/java/species/Grow.java index df3bb3f5fdd502d893a9f7e752384cb7329eb9b4..f463168b43f72a891c3d98b03997b2ef0c47e8ab 100644 --- a/src/main/java/species/Grow.java +++ b/src/main/java/species/Grow.java @@ -16,6 +16,7 @@ import miscellaneous.Miscellaneous; import org.openide.util.lookup.ServiceProvider; +import species.DiadromousFish.Gender; import species.DiadromousFish.Stage; import umontreal.iro.lecuyer.probdist.NormalDist; import umontreal.iro.lecuyer.randvar.NormalGen; @@ -23,11 +24,72 @@ import umontreal.iro.lecuyer.randvar.NormalGen; @ServiceProvider(service = AquaNismsGroupProcess.class) public class Grow extends AquaNismsGroupProcess<DiadromousFish, DiadromousFishGroup> { + /** + * temperature minimum for growth + * @unit °C + */ private double tempMinGrow = 3.; + + /** + * temperature maximum for growth + * @unit °C + */ private double tempMaxGrow = 26.; + + /** + * temperature optimal for growth + * @unit °C + */ private double tempOptGrow = 17.; - private double kOpt = 0.3; - private double sigmaDeltaLVonBert = 0.2; // random value... has to be fixed with literature + + /** + * K, Brody growth rate at optimal temperature + * L = Linf *(1-exp(-K*(t-t0)) + * @unit year -1 + */ + private double kOptForFemale= 0.3; + + + /** + * @return the kOptForFemale + */ + public double getkOptForFemale() { + return kOptForFemale; + } + + /** + * @param kOptForFemale the kOptForFemale to set + */ + public void setkOptForFemale(double kOptForFemale) { + this.kOptForFemale = kOptForFemale; + } + + /** + * @return the kOptForMale + */ + public double getkOptForMale() { + return kOptForMale; + } + + /** + * @param kOptForMale the kOptForMale to set + */ + public void setkOptForMale(double kOptForMale) { + this.kOptForMale = kOptForMale; + } + + /** + * K, Brody growth rate at optimal temperature + * L = Linf *(1-exp(-K*(t-t0)) + * @unit year -1 + */ + private double kOptForMale= 0.3; + + /** + * standart deviation for the lognormal random draw of growth increment + * @unit cm + */ + private double sigmaDeltaLVonBert = 0.2; private transient NormalGen genNormal; @@ -37,45 +99,68 @@ public class Grow extends AquaNismsGroupProcess<DiadromousFish, DiadromousFishGr @Override @InitTransientParameters public void initTransientParameters(Pilot pilot) { - super.initTransientParameters(pilot); + super.initTransientParameters(pilot); genNormal = new NormalGen( pilot.getRandomStream(), new NormalDist(0., 1.)); } + + @Override public void doProcess(DiadromousFishGroup group) { - for(Basin basin : group.getEnvironment().getBasins()){ - if (basin.getFishs(group)!=null) for(DiadromousFish fish : basin.getFishs(group)){ - double muDeltaLVonBert = 0.; - double kVonBert = 0.; - double growthIncrement = 0.; - //Grow - // 1) We calculate the kVonBert - - if (group.getKOpt()==Double.NaN){ - kVonBert = kOpt * - Miscellaneous.temperatureEffect(fish.getPosition().getCurrentTemperature(group.getPilot()), tempMinGrow, tempOptGrow, tempMaxGrow); - } else { - kVonBert = group.getKOpt() * - Miscellaneous.temperatureEffect(fish.getPosition().getCurrentTemperature(group.getPilot()), tempMinGrow, tempOptGrow, tempMaxGrow); - } - - // 2) We update the size of the fish - if (fish.getLength() < group.getLinfVonBert()){ - muDeltaLVonBert = Math.log((group.getLinfVonBert() - fish.getLength()) * (1 - Math.exp(-kVonBert * Time.getSeasonDuration()))) - (Math.pow(sigmaDeltaLVonBert,2))/2; - growthIncrement = Math.exp(genNormal.nextDouble()*sigmaDeltaLVonBert + muDeltaLVonBert); - fish.setLength(Math.min(group.getLinfVonBert(), fish.getLength() + growthIncrement)); - }else{ - fish.setLength(group.getLinfVonBert()); - } - - if (fish.getStage() == Stage.IMMATURE){ - if (fish.getLength() > group.getlFirstMaturity()){ - fish.setStage(Stage.MATURE); + for(Basin basin : group.getEnvironment().getBasins()){ + if (basin.getFishs(group)!=null) + for(DiadromousFish fish : basin.getFishs(group)){ + double muDeltaLVonBert = 0.; + double kVonBert = 0.; + double growthIncrement = 0.; + + // 1) calculate the kVonBert + //System.out.println(this.getKOpt(fish, group) ); + kVonBert = this.getKOpt(fish, group) * + Miscellaneous.temperatureEffect(fish.getPosition().getCurrentTemperature(group.getPilot()), tempMinGrow, tempOptGrow, tempMaxGrow); + + // 2) Update the fish length with a lognormal normal draw of increment + // limit the fish length to Linf + if (fish.getLength() < group.getLinfVonBert(fish)){ + muDeltaLVonBert = Math.log((group.getLinfVonBert(fish) - fish.getLength()) * (1 - Math.exp(-kVonBert * Time.getSeasonDuration()))) - (sigmaDeltaLVonBert*sigmaDeltaLVonBert)/2; + growthIncrement = Math.exp(genNormal.nextDouble()*sigmaDeltaLVonBert + muDeltaLVonBert); + + + fish.setLength(Math.min(group.getLinfVonBert(fish), fish.getLength() + growthIncrement)); + } + else { + fish.setLength(group.getLinfVonBert(fish)); + } + //System.out.println(fish.getAge() + " -> "+ fish.getLength() + " ("+fish.getStage()+"): "+ growthIncrement); + // test if fish become mature + if (fish.getStage() == Stage.IMMATURE && fish.getLength() > group.getlFirstMaturity(fish)){ + fish.setStage(Stage.MATURE); + } + //System.out.println("la temp�rature du lieu de vie du poisson est :" + fish.getPosition().getCurrentTemperature() + ", la saison est :" + Time.getSeason() + " et sa nouvelle taille est :" + fish.getLength()); } - } - //System.out.println("la température du lieu de vie du poisson est :" + fish.getPosition().getCurrentTemperature() + ", la saison est :" + Time.getSeason() + " et sa nouvelle taille est :" + fish.getLength()); - } - } + } + } + + /** + * @param fish + * @param group + * @return the Brody coeff from Diadromousgroup if exists or from this grow process + * depends of the fish gender .In case of undifferentiaced fish, the mean for male and female is considered + */ + public double getKOpt(DiadromousFish fish, DiadromousFishGroup group) { + double kOpt = group.getKOpt(fish); + if (Double.isNaN(kOpt)){ // no definition for the group + if (fish.getGender() == Gender.FEMALE) + kOpt = kOptForFemale; + else if (fish.getGender() == Gender.MALE) + kOpt = kOptForMale; + else + kOpt= (kOptForFemale + kOptForMale) / 2.; + } + + return kOpt; + + } } diff --git a/src/main/java/species/IdentifyPopulation.java b/src/main/java/species/IdentifyPopulation.java index 68dea76c2ef15b90f8b5201a72be7c4b73d39ef2..f6e651d02dfa113db8ceb8b4b71d28b0a10d4960 100644 --- a/src/main/java/species/IdentifyPopulation.java +++ b/src/main/java/species/IdentifyPopulation.java @@ -69,7 +69,8 @@ public class IdentifyPopulation extends AquaNismsGroupProcess<DiadromousFish, Di bW = new BufferedWriter(new FileWriter(new File(group.getOutputPath()+ fileNameOutput +group.getSimulationId()+ ".csv"))); - BasinNetworkReal nbr= (BasinNetworkReal) pilot.getAquaticWorld().getEnvironment(); + // BasinNetworkReal nbr= (BasinNetworkReal) pilot.getAquaticWorld().getEnvironment(); + BasinNetwork nbr= group.getEnvironment(); bW.write("year"+sep+"migrationBasin"); for (String basinName : nbr.getRiverBasinNames()){ bW.write(sep+basinName); diff --git a/src/main/java/species/MigrateToSea.java b/src/main/java/species/MigrateToSea.java index 95fbddeaec73451a1213cafe3d859af86596d9a5..65720a77827eef4d0c0fbbb6dce658fa92a5b2ec 100644 --- a/src/main/java/species/MigrateToSea.java +++ b/src/main/java/species/MigrateToSea.java @@ -8,9 +8,16 @@ import environment.RiverBasin; import environment.Time; import environment.Time.Season; import fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess; + +import java.io.BufferedWriter; +import java.io.IOException; import java.util.ArrayList; +import java.util.Hashtable; import java.util.List; +import java.util.Map; + import miscellaneous.Duo; +import species.DiadromousFish.Stage; import org.openide.util.lookup.ServiceProvider; @ServiceProvider(service = AquaNismsGroupProcess.class) @@ -18,6 +25,8 @@ public class MigrateToSea extends AquaNismsGroupProcess<DiadromousFish, Diadromo private Season seaMigrationSeason = Season.SUMMER; + private boolean displayFluxesOnConsole = true; + public static void main(String[] args) { System.out.println((new XStream(new DomDriver())) .toXML(new MigrateToSea())); @@ -28,19 +37,65 @@ public class MigrateToSea extends AquaNismsGroupProcess<DiadromousFish, Diadromo if (Time.getSeason(group.getPilot()) == seaMigrationSeason ){ Basin destination; - List<Duo<DiadromousFish,Basin>> fishesToMove = new ArrayList<Duo<DiadromousFish,Basin>>(); - for (int i = 0; i < group.getEnvironment().getRiverBasins().length; i++) { - RiverBasin basin = group.getEnvironment().getRiverBasins()[i]; - - List<DiadromousFish> fishes = basin.getFishs(group); - if (fishes!=null) for (DiadromousFish fish : fishes) { - destination = group.getEnvironment().getAssociatedSeaBasin(fish.getPosition()); - fishesToMove.add(new Duo<DiadromousFish, Basin>(fish, destination)); - } - } - for (Duo<DiadromousFish,Basin> duo : fishesToMove) { - duo.getFirst().moveTo(group.getPilot(), duo.getSecond(), group); - } - } + + //On cr�er la Map pour stocker les flux d'export + Map<String, Double> totalOutputFluxes = new Hashtable<String, Double>(); + + List<Duo<DiadromousFish,Basin>> fishesToMove = new ArrayList<Duo<DiadromousFish,Basin>>(); + for (int i = 0; i < group.getEnvironment().getRiverBasins().length; i++) { + RiverBasin basin = group.getEnvironment().getRiverBasins()[i]; + //Fish move to sea and compute the related export of nutrients + List<DiadromousFish> fishes = basin.getFishs(group); + + // ON r�-initialise notre map pour chauqe bassin + for (String nutrient : group.getNutrientRoutine().getNutrientsOfInterest()) { + totalOutputFluxes.put(nutrient, 0.); + } + totalOutputFluxes.put("biomass", 0.); //cr�ation de la biomasse + + if (fishes!=null) { + for (DiadromousFish fish : fishes) { + destination = group.getEnvironment().getAssociatedSeaBasin(fish.getPosition()); + fishesToMove.add(new Duo<DiadromousFish, Basin>(fish, destination)); //Mentionne la sortie d'un poisson de la boucle + + double biomass = group.getNutrientRoutine().getWeight(fish) * fish.getAmount(); + + if (fish.getStage()==Stage.IMMATURE) { + Map <String, Double> aFluxExportedByJuveniles= group.getNutrientRoutine().computeNutrientsExportForJuveniles(fish); + for (String nutrient: aFluxExportedByJuveniles.keySet()) { + totalOutputFluxes.put(nutrient,totalOutputFluxes.get(nutrient) + aFluxExportedByJuveniles.get(nutrient) * fish.getAmount()); + } + + totalOutputFluxes.put("biomass", totalOutputFluxes.get("biomass") + biomass); + } + } + } + + for (Duo<DiadromousFish,Basin> duo : fishesToMove) { + duo.getFirst().moveTo(group.getPilot(), duo.getSecond(), group); //on d�place les poissons dans le fichier MoveTo et on d�note la destination du poisson. + } + + if (displayFluxesOnConsole) + System.out.println(group.getPilot().getCurrentTime() + "; " + Time.getYear(group.getPilot()) + ";" + Time.getSeason(group.getPilot()) + ";EXPORT;" + + basin.getName() + "; " + totalOutputFluxes); + + BufferedWriter bW = group.getbWForFluxes(); + if ( bW != null) { + try { + + bW.write(group.getPilot().getCurrentTime() + "; " + Time.getYear(group.getPilot()) + ";" + Time.getSeason(group.getPilot()) + +";"+ basin.getName() + ";" + basin.getJuvenileNumber() + ";EXPORT; NONE"); + bW.write(";" + totalOutputFluxes.get("biomass")); + for (String nutrient : group.getNutrientRoutine().getNutrientsOfInterest()) { + bW.write(";" + totalOutputFluxes.get(nutrient)); + } + bW.write("\n"); + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } } -} +} \ No newline at end of file diff --git a/src/main/java/species/NutrientRoutine.java b/src/main/java/species/NutrientRoutine.java new file mode 100644 index 0000000000000000000000000000000000000000..8aebf82ace1ddc7801312e89e87c636f5883865f --- /dev/null +++ b/src/main/java/species/NutrientRoutine.java @@ -0,0 +1,534 @@ +/** + * + * @author Camille Poulet, Patrick Lambert + * @copyright Copyright (c) 2019, Irstea + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + + */ +package species; + +import environment.SeaBasin; +import fr.cemagref.simaqualife.pilot.Pilot; +import species.DiadromousFish.Gender; +import species.DiadromousFish.Stage; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Map; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.DomDriver; + + +/** + * @author camille.poulet + * + */ +public class NutrientRoutine { + + + private static enum SpawningPosition {PRE,POST}; // on cr�er un static pour r�server une m�me classe m�moire pour toutes les instances + + //private static enum Gender {UNDIFFERENCIED, FEMALE, MALE}; + + private ArrayList<String> nutrientsOfInterest; + + private double residenceTime; + + private Map<String, Double> excretionRate; + + /** + * Main feature for weight (g) computation before spawning i.e. gametes expelling according to gender, for a given length (cm) + * //Voir pour un retour � la ligne lors du commentaire + * key gender + * value + * key feature + * value value + */ + private Map <DiadromousFish.Gender,Map<String, Double>> fishFeaturesPreSpawning; + + private Map <DiadromousFish.Gender, Map<String, Double>> fishFeaturesPostSpawning; + + private Map<String, Double> juvenileFeatures; + + + /** + * Weight of gametes spawned for both males and females + * key gender + * value g + * usually computed as the difference between unspawned gonad (inbound) and spawned gonad (outbound; "spent gonad") + */ + //private Map <DiadromousFish.Gender, Double> spawnedGametesWeight; + + + /** + * chemical composition of carcass before gametes expelling (before spawning) i.e. soma + gonads + gametes + * <key> gender + * <value> + * <key> chemical element + * <value> value ratio element / (total wet weight) g/g + */ + private Map<DiadromousFish.Gender,Map<String,Double>> compoCarcassPreSpawning; + + //package permettant la cr�ation d'une table de hachage ie fonctionnant en cl� -valeur. Cl� unique, mais valeur peut �tre associ�e � plusieurs cl�s; + //La class d'objet Map a pour point faible la taille des donn�es � stocker. Plus on a de valeur dans la table, plus c'est lourd et lent! Donc, trouver un compromis entre temps de calcul et espace. + //N'accepte pas la valeur nulle et thread safe i.e. utilisable simultan�ment par plusieurs �l�ments du programme. + + /** + * chemical composition of carcass after spawning i.e. soma + spent gonad (without gametes) + * <value> + * <key> chemical element + * <value> value + */ + private Map<DiadromousFish.Gender, Map<String, Double>> compoCarcassPostSpawning; + + /** + * chemical composition of gametes estimated from the MIGADO dataset (BDalosesBruch) + * <key> gender + * <value> + * <key> chemical element + * <value> value + */ + private Map<DiadromousFish.Gender, Map<String,Double>> compoGametes; + + // For juveniles - Based on Taverny (1991) + + /** + * chemical composition of juveniles + * <key> chemical element + * <value> value + */ + + private Map<String,Double> compoJuvenile; + + + public NutrientRoutine() { + + } + + /** + * Constructor based on the 5 Map of fish composition + * @param fishFeaturesPreSpawning + * @param compoCarcassPreSpawning + * @param compoCarcassPostSpawning + * @param compoGametes + * @param compoJuvenile + */ + public NutrientRoutine(ArrayList<String> nutrientsOfInterest, + + double residenceTime, + Map <String, Double> excretionRate, + Map<DiadromousFish.Gender, Map<String, Double>> fishFeaturesPreSpawning, + Map<DiadromousFish.Gender, Map<String, Double>> fishFeaturesPostSpawning, + Map<DiadromousFish.Gender, Map<String, Double>> compoCarcassPreSpawning, + Map<DiadromousFish.Gender, Map<String, Double>> compoCarcassPostSpawning, + Map<DiadromousFish.Gender, Map<String, Double>> compoGametes, + Map<String, Double> juvenileFeatures, + Map<String, Double> compoJuvenile) + { + super(); + this.nutrientsOfInterest = nutrientsOfInterest; + this.excretionRate = excretionRate; + this.residenceTime = residenceTime; + this.fishFeaturesPreSpawning = fishFeaturesPreSpawning; + this.fishFeaturesPostSpawning = fishFeaturesPostSpawning; + this.compoCarcassPreSpawning = compoCarcassPreSpawning; + this.compoCarcassPostSpawning = compoCarcassPostSpawning; + this.compoGametes = compoGametes; + this.juvenileFeatures = juvenileFeatures; + this.compoJuvenile = compoJuvenile; + + } + + /** + * compute the nutrient fluxes for a single fish (in the super individual) + * that dies before spawning + * @param fish + */ + public Map<String,Double> computeNutrientsInputForDeathBeforeSpawning(DiadromousFish fish, ArrayList<String> nutrientsOfInterest) { + + Map<String,Double> nutrientsInput = new Hashtable<String, Double>(); // On cr�er ici une Map, classe m�re des hashtable (Homme = classe mere ie Map//Jules = hashtable) + for (String nutrient : nutrientsOfInterest) { + + if (fish.getStage()== Stage.MATURE) { + double totalWeightPre = this.getWeight(fish,SpawningPosition.PRE); + double carcass = totalWeightPre + * compoCarcassPreSpawning.get(fish.getGender()).get(nutrient); + double excretion = totalWeightPre + * residenceTime + * excretionRate.get(nutrient) ; + double nutrientImport = carcass + excretion; + + nutrientsInput.put(nutrient, nutrientImport); + } + else { + nutrientsInput.put(nutrient, 0.); + } + } + + //TODO Multiply by fish amount + return nutrientsInput; + } + + public Map<String,Double> computeNutrientsInputForDeathBeforeSpawning(DiadromousFish fish) { + + return computeNutrientsInputForDeathBeforeSpawning(fish,this.nutrientsOfInterest); + } + + /** + * compute the nutrient fluxes for a single fish (in the super individual) + * that dies after spawning (gametes expelling) + * Considering that a fish died after reproduction have the same nutrient contribution that a fish died before reproduction, we use the same coefficient for weight computation. + * @param fish + * @return nutrientsInput + */ + public Map<String, Double> computeNutrientsInputForDeathAfterSpawning(DiadromousFish fish, ArrayList<String> nutrientsOfInterest) { + + Map<String,Double> nutrientsInput = new Hashtable<String,Double>(); + for (String nutrient : nutrientsOfInterest) { + + if (fish.getStage()== Stage.MATURE) { + + double totalWeightPost = this.getWeight(fish, SpawningPosition.POST); + double carcass = totalWeightPost + * compoCarcassPostSpawning.get(fish.getGender()).get(nutrient); + //double gametes = (totalWeightPre - totalWeightPost) FAUX car perte de poids somatique due a la reproduction + double gametes = this.getGonadWeight(fish, SpawningPosition.PRE) - this.getGonadWeight(fish, SpawningPosition.POST) + *compoGametes.get(fish.getGender()).get(nutrient); + double excretion = totalWeightPost + * residenceTime + * excretionRate.get(nutrient); + double nutrientImport = carcass + gametes + excretion; + + nutrientsInput.put(nutrient,nutrientImport); + } + else { + nutrientsInput.put(nutrient,0.); + } + } + return nutrientsInput; + } + + public Map<String, Double> computeNutrientsInputForDeathAfterSpawning(DiadromousFish fish){ + + return computeNutrientsInputForDeathAfterSpawning(fish, this.nutrientsOfInterest); + } + /** + * compute the nutrient fluxes for a single fish (in the super individual) + * that survives after spawning + * Map: model output = element of interest ie string + double ie the quantification of this fluxes. + * @return nutrientsInput + */ + public Map<String,Double>computeNutrientsInputForSurvivalAfterSpawning(DiadromousFish fish, ArrayList<String> nutrientsOfInterest) { + + Map<String,Double> nutrientsInput = new Hashtable<String,Double>(); + for (String nutrient: nutrientsOfInterest) { + if (fish.getStage()==Stage.MATURE) { + + //TODO Fix with new data + double totalWeightPost = this.getWeight(fish, SpawningPosition.POST); + //Gamete compositions depends on sex. + + double gametes = this.getGonadWeight(fish, SpawningPosition.PRE) - this.getGonadWeight(fish, SpawningPosition.POST) + * compoGametes.get(fish.getGender()).get(nutrient); + double excretion = totalWeightPost + * residenceTime + * excretionRate.get(nutrient); + double nutrientImport = gametes + excretion; + + nutrientsInput.put(nutrient, nutrientImport); + } + else { + nutrientsInput.put(nutrient,0.); + } + } + return nutrientsInput; + } + + public Map<String,Double>computeNutrientsInputForSurvivalAfterSpawning(DiadromousFish fish) { + + return computeNutrientsInputForSurvivalAfterSpawning(fish, this.nutrientsOfInterest); + } + + + public Map<String,Double> computeNutrientsExportForJuveniles(DiadromousFish juvenileFish, ArrayList<String>nutrientsOfInterest) { + Map<String,Double> nutrientsExport = new Hashtable<String,Double>(); + for(String nutrient: nutrientsOfInterest) { + if(juvenileFish.getStage()==Stage.IMMATURE) { + + double JuvenileMass = this.getWeight(juvenileFish); + nutrientsExport.put(nutrient, JuvenileMass * compoJuvenile.get(nutrient)); + } + } + + return nutrientsExport; + } + + public Map<String,Double> computeNutrientsExportForJuveniles(DiadromousFish juvenileFish){ + return computeNutrientsExportForJuveniles(juvenileFish, this.nutrientsOfInterest); + + } + + /** + * Compute the weight for a fish with length (cm) + * @param fish + * @return weight (g) + */ + public double getWeight (DiadromousFish fish, SpawningPosition spawningPosition) { + + double weight = 0.; + if (fish.getStage()==Stage.IMMATURE) + weight = juvenileFeatures.get("aLW") * Math.pow(fish.getLength(),juvenileFeatures.get("bLW")); + else //Stage.MATURE + if (spawningPosition == SpawningPosition.PRE) + weight = fishFeaturesPreSpawning.get(fish.getGender()).get("aLW") * Math.pow(fish.getLength(), fishFeaturesPreSpawning.get(fish.getGender()).get("bLW") ); + else + weight = fishFeaturesPostSpawning.get(fish.getGender()).get("aLW") * Math.pow(fish.getLength(), fishFeaturesPostSpawning.get(fish.getGender()).get("bLW")); + + return weight; + } + + + /** + * Compute the weight for a fish with length (cm) + * @param fish + * @return weight (g) + */ + public double getWeight (DiadromousFish fish) { + + return getWeight (fish, SpawningPosition.PRE); + } + + public double getGonadWeight (DiadromousFish fish, SpawningPosition spawningPosition) { + + double gonadWeight = 0.; + if (fish.getStage()==Stage.MATURE) { + if (spawningPosition == SpawningPosition.PRE) + gonadWeight = Math.exp(fishFeaturesPreSpawning.get(fish.getGender()).get("aLW_Gonad") + + fishFeaturesPreSpawning.get(fish.getGender()).get("bLW_Gonad") * Math.log(fish.getLength())); + else { + gonadWeight = Math.exp(fishFeaturesPostSpawning.get(fish.getGender()).get("aLW_Gonad") + + fishFeaturesPostSpawning.get(fish.getGender()).get("bLW_Gonad") * Math.log(fish.getLength())); + } + } + return gonadWeight; + } + + + /** + * Compute the gonad weight for a fish with length (cm) to compute the gamete emission (g). + * @param fish + * @return weight (g) + */ + public double getGonadWeight (DiadromousFish fish) { + + return getGonadWeight (fish, SpawningPosition.PRE); + } + + + + + public ArrayList<String> getNutrientsOfInterest() { + return nutrientsOfInterest; + } + + /** + * @param args + */ + /** + * @param args + */ + /** + * @param args + */ + public static void main(String[] args) { + + + double aResidenceTime =30; + + System.out.println("aResidenceTime: " + aResidenceTime); // + + + Map <String, Double> anExcretionRate = new Hashtable <String, Double>(); + anExcretionRate.put("N", 24.71E-6); //values from Barber et al, Alewifes in ug/g wet mass/hour : convertit en g + anExcretionRate.put("P", 2.17E-6); //values from Barber et al, Alewifes in ug/g wet mass/hour: convertit en g + + System.out.println("anExcretionRate: " + anExcretionRate.toString()); // + + /* + * A feature pre spawning + */ + Map<Gender, Map<String, Double>> aFeaturePreSpawning = new Hashtable<DiadromousFish.Gender, Map<String,Double>>(); + + /* + * For females + */ + Map<String,Double> aFeature = new Hashtable<String,Double>(); + + aFeature.put("bLW", 3.3429); //From Taverny + aFeature.put("aLW", 1.2102E-6 * Math.pow(10., aFeature.get("bLW"))); //weight size relationship -- Conversion des g/mm en g.cm (from Taverny, 1991) + aFeature.put("bLW_Gonad", 2.6729); // issu de la relation taille - poids des gonades Bruch + aFeature.put("aLW_Gonad", -5.2425); // issu de la relation taille - poids des gonades Bruch + + aFeaturePreSpawning.put(Gender.FEMALE, aFeature); + + /* + * For males + */ + aFeature = new Hashtable<String,Double>(); + aFeature.put("bLW", 3.2252); + aFeature.put("aLW", 2.4386E-6 * Math.pow(10., aFeature.get("bLW"))); //weight size relationship from Taverny -- Conversion des g/mm en g.cm (from Taverny, 1991) + aFeature.put("bLW_Gonad", 3.3838); + aFeature.put("aLW_Gonad", -8.8744); + aFeaturePreSpawning.put(Gender.MALE,aFeature); + + System.out.println("aFeaturePreSpawning: " + aFeaturePreSpawning.toString()); // + + /* + * a Feature post Spawning + */ + Map<Gender, Map<String, Double>> aFeaturePostSpawning = new Hashtable<DiadromousFish.Gender, Map<String,Double>>(); + + /* + * For females + */ + aFeature = new Hashtable<String,Double>(); + aFeature.put("aLW", Math.exp(-4.3276)); //weight size relationship computed from BDalosesBruch + aFeature.put("bLW", 2.9418); + aFeature.put("aLW_Gonad", -6.6234); // issu de la relation taille - poids des gonades Bruch + aFeature.put("bLW_Gonad", 2.8545); // issu de la relation taille - poids des gonades Bruch + //aFeature.put("GSI",0.10); //From BDalosesBruch + //aFeature.put("aLW",aFeaturePreSpawning.get(Gender.FEMALE).get("aLW")/(1+aFeature.get("GSI"))); // parametre "a" de la relation taille/poids avec Lt en cm - Traduit la condition + //aFeature.put("bLW",aFeaturePreSpawning.get(Gender.FEMALE).get("bLW"));// parametre "b" de la relation taille/poids - Coefficient d'allometrie + aFeaturePostSpawning.put(Gender.FEMALE, aFeature); + + /* + * For males + */ + aFeature = new Hashtable<String,Double>(); + + aFeature.put("aLW", Math.exp(-4.5675));// parametre "a" de la relation taille/poids - Coefficient d'allometrie + aFeature.put("bLW", 2.9973); + aFeature.put("aLW_Gonad", -11.285); // issu de la relation taille - poids des gonades Bruch + aFeature.put("bLW_Gonad", 3.8331); // issu de la relation taille - poids des gonades Bruch + //aFeature.put("GSI",.05); From BDalosesBruch + //aFeature.put("aLW",aFeaturePreSpawning.get(Gender.MALE).get("aLW")/(1+aFeature.get("GSI"))); + //aFeature.put("bLW",aFeaturePreSpawning.get(Gender.MALE).get("bLW")); + aFeaturePostSpawning.put(Gender.MALE,aFeature); + + System.out.println("aFeaturePostSpawning: " + aFeaturePostSpawning.toString()); + + // carcass composition for fish before spawning + Map<Gender, Map<String, Double>> aCompoCarcassPreSpawning = new Hashtable<DiadromousFish.Gender,Map<String,Double>>(); + Map<String,Double> aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 2.958 / 100.); //On remplit une collection avec un put. Values from Haskell (2018) Alosa sapidissima (%) + aCompo.put("P", 0.673 / 100.); //Values from Haskell (2018) Alosa sapidissima (%) + aCompoCarcassPreSpawning.put(Gender.FEMALE,aCompo); + + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 2.941 / 100.); //Values from Haskell (2018) Alosa sapidissima (%) + aCompo.put("P", 0.666 / 100.);// Values from Haskell (2018) Alosa sapidissima (%) + aCompoCarcassPreSpawning.put(Gender.MALE,aCompo); + + System.out.println("aCompoCarcassPreSpawning: " + aCompoCarcassPreSpawning.toString()); // + + // carcass composition for fish after spawning + Map<Gender, Map<String, Double>> aCompoCarcassPostSpawning = new Hashtable<DiadromousFish.Gender,Map<String,Double>>(); + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 3.216 / 100.); //On remplit une collection avec un put. + aCompo.put("P", 0.997 / 100.); + aCompoCarcassPostSpawning.put(Gender.FEMALE,aCompo); + + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 2.790 / 100.); // From Haskel et al, 2017 + aCompo.put("P", 0.961 / 100.); + aCompoCarcassPostSpawning.put(Gender.MALE,aCompo); + + System.out.println("aCompoCarcassPostSpawning: " + aCompoCarcassPostSpawning.toString()); // + + // Gametes composition approximated by the difference between gonads weight before and after spawning. + Map<Gender, Map<String, Double>> aCompoGametes = new Hashtable<DiadromousFish.Gender,Map<String,Double>>(); + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 3.242 / 100.); //On remplit une collection avec un put. From Haskel et al, 2018. + aCompo.put("P", 0.320 / 100.); // Haskel = %P, N, ici ratio donc divise par 100 + aCompoGametes.put(Gender.FEMALE,aCompo); + + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 3.250 / 100.); // Approxim�e par la compo des gonades + aCompo.put("P", 0.724 / 100.); + aCompoGametes.put(Gender.MALE,aCompo); + + System.out.println("aCompoGametes:" + aCompoGametes.toString()); // + + // features for juveniles + + Map<String,Double> aJuvenileFeatures = new Hashtable<String, Double>(); + aJuvenileFeatures.put("bLW",3.0306); + aJuvenileFeatures.put("aLW",Math.exp(-11.942) * Math.pow(10., aJuvenileFeatures.get("bLW"))); + + System.out.println("aJuvenileFeatures: " + aJuvenileFeatures.toString()); + + // carcass composition for juveniles fish + Map<String, Double> aCompoJuveniles = new Hashtable<String,Double>(); + aCompoJuveniles.put("N", 2.803 / 100.); //On remplit une collection avec un put. %N in wet weight (Haskell et al, 2017) on Alosa sapidissima + aCompoJuveniles.put("P", 0.887 / 100.); //%P in wet weight (from Haskell et al, 2017) on Alosa sapidissima + + System.out.println("aCompoJuveniles: " + aCompoJuveniles.toString()); + + ArrayList <String> nutrientsOfInterest= new ArrayList <String>(); + nutrientsOfInterest.add("N"); + nutrientsOfInterest.add("P"); + + System.out.println("nutrientsOfInterest: " + nutrientsOfInterest); + + + NutrientRoutine fn = new NutrientRoutine(nutrientsOfInterest,aResidenceTime, anExcretionRate, aFeaturePreSpawning, aFeaturePostSpawning, + aCompoCarcassPreSpawning, aCompoCarcassPostSpawning, aCompoGametes, + aJuvenileFeatures, aCompoJuveniles); + + SeaBasin basin = new SeaBasin(0,"Bidon",10.,12., 14.,12.); //il faut aller dans "SeaBasin" dans "environement et regarder comment est construit le constructeur. Il lui faut ici un rang, un nom de bassin versant, et des temp�rature pour chaque saison + Pilot pilot = new Pilot (); + DiadromousFish fishFemale = new DiadromousFish (pilot, basin, 52., 1L, Gender.FEMALE); //Idem ici, on regarde comment est construit DiadromousFih et on lui donne les valeur de ce qu'il nous demande. + fishFemale.setStage(Stage.MATURE); + DiadromousFish fishMale = new DiadromousFish (pilot, basin, 47., 1L, Gender.MALE); //Idem ici, on regarde comment est construit DiadromousFih et on lui donne les valeur de ce qu'il nous demande. + fishMale.setStage(Stage.MATURE); + DiadromousFish juvenileFish = new DiadromousFish(pilot,basin,7.0,1L,Gender.UNDIFFERENCIED); + juvenileFish.setStage(Stage.IMMATURE); + + System.out.println(); // affiche une ligne blanche + System.out.println(fishFemale.getGender() + ": " + fishFemale.getLength() + " cm " + fn.getWeight(fishFemale, SpawningPosition.PRE)+ " g " + fn.getWeight(fishFemale, SpawningPosition.POST)); + System.out.println("\tNutrients Fluxes for death before spawning " + fn.computeNutrientsInputForDeathBeforeSpawning(fishFemale).toString()); + System.out.println("\tNutrients Fluxes for death after spawning " + fn.computeNutrientsInputForDeathAfterSpawning(fishFemale).toString()); + System.out.println("\tNutrients Fluxes for survival " + fn.computeNutrientsInputForSurvivalAfterSpawning(fishFemale).toString()); + + System.out.println(fishMale.getGender() + ": " + fishMale.getLength() + " cm " + fn.getWeight(fishMale, SpawningPosition.PRE)+ " g " + fn.getWeight(fishMale, SpawningPosition.POST)); + System.out.println("\tNutrients Fluxes for death before spawning " + fn.computeNutrientsInputForDeathBeforeSpawning(fishMale).toString()); + System.out.println("\tNutrients Fluxes for death after spawning " + fn.computeNutrientsInputForDeathAfterSpawning(fishMale).toString()); + System.out.println("\tNutrients Fluxes for survival " + fn.computeNutrientsInputForSurvivalAfterSpawning(fishMale).toString()); + + System.out.println(juvenileFish.getStage() + ": " + juvenileFish.getLength() + " cm " + fn.getWeight(juvenileFish)+ " g "); + System.out.println("\tNutrients Fluxes for juveniles " + fn.computeNutrientsExportForJuveniles(juvenileFish).toString()); + + + + + /* Create XML file + * + */ + System.out.println((new XStream(new DomDriver())).toXML(fn)); + + } +} + + diff --git a/src/main/java/species/NutrientRoutineEssay.java b/src/main/java/species/NutrientRoutineEssay.java new file mode 100644 index 0000000000000000000000000000000000000000..1fc44ab3ede1b9cbba21a81ed705b1214f9941ae --- /dev/null +++ b/src/main/java/species/NutrientRoutineEssay.java @@ -0,0 +1,437 @@ + +package species; + +import environment.SeaBasin; +import fr.cemagref.simaqualife.pilot.Pilot; +import species.DiadromousFish.Gender; +import species.DiadromousFish.Stage; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Map; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.DomDriver; + + +/** + * @author camille.poulet + * + */ +public class NutrientRoutineEssay { + + + private static enum SpawningPosition {PRE,POST}; // on créer un static pour réserver une même classe mémoire pour toutes les instances + + //private static enum Gender {UNDIFFERENCIED, FEMALE, MALE}; + + private ArrayList<String> nutrientsOfInterest; + + private double residenceTime; + + private Map<String, Double> excretionRate; + + /** + * Main feature for weight (g) computation before spawning i.e. gametes expelling according to gender, for a given length (cm) + * //Voir pour un retour à la ligne lors du commentaire + * key gender + * value + * key feature + * value value + */ + private Map <DiadromousFish.Gender,Map<String, Double>> fishFeaturesPreSpawning; + + private Map <DiadromousFish.Gender, Map<String, Double>> fishFeaturesPostSpawning; + + private Map<String, Double> juvenileFeatures; + + + /** + * Weight of gametes spawned for both males and females + * key gender + * value g + * usually computed as the difference between unspawned gonad (inbound) and spawned gonad (outbound; "spent gonad") + */ + //private Map <DiadromousFish.Gender, Double> spawnedGametesWeight; + + + /** + * chemical composition of carcass before gametes expelling (before spawning) i.e. soma + gonads + gametes + * <key> gender + * <value> + * <key> chemical element + * <value> value ratio element / (total wet weight) g/g + */ + private Map<DiadromousFish.Gender,Map<String,Double>> compoCarcassPreSpawning; + + //package permettant la création d'une table de hachage ie fonctionnant en clé -valeur. Clé unique, mais valeur peut être associée à plusieurs clés; + //La class d'objet Map a pour point faible la taille des données à stocker. Plus on a de valeur dans la table, plus c'est lourd et lent! Donc, trouver un compromis entre temps de calcul et espace. + //N'accepte pas la valeur nulle et thread safe i.e. utilisable simultanément par plusieurs éléments du programme. + + + /** + * chemical composition of gametes estimated from the MIGADO dataset (BDalosesBruch) + * <key> gender + * <value> + * <key> chemical element + * <value> value + */ + private Map<DiadromousFish.Gender, Map<String,Double>> compoGametes; + + // For juveniles - Based on Taverny (1991) + + /** + * chemical composition of juveniles + * <key> chemical element + * <value> value + */ + + private Map<String,Double> compoJuvenile; + + + + /** + * Constructor based on the 5 Map of fish composition + * @param fishFeaturesPreSpawning + * @param compoCarcassPreSpawning + * @param compoCarcassPostSpawning + * @param compoGametes + * @param compoJuvenile + */ + public NutrientRoutineEssay(ArrayList<String> nutrientsOfInterest, + + double residenceTime, + Map <String, Double> excretionRate, + Map<DiadromousFish.Gender, Map<String, Double>> fishFeaturesPreSpawning, + Map<DiadromousFish.Gender, Map<String, Double>> fishFeaturesPostSpawning, + Map<DiadromousFish.Gender, Map<String, Double>> compoCarcassPreSpawning, + Map<DiadromousFish.Gender, Map<String, Double>> compoGametes, + Map<String, Double> juvenileFeatures, + Map<String, Double> compoJuvenile) + { + super(); + this.nutrientsOfInterest = nutrientsOfInterest; + this.excretionRate = excretionRate; + this.residenceTime = residenceTime; + this.fishFeaturesPreSpawning = fishFeaturesPreSpawning; + this.fishFeaturesPostSpawning = fishFeaturesPostSpawning; + this.compoCarcassPreSpawning = compoCarcassPreSpawning; + this.compoGametes = compoGametes; + this.juvenileFeatures = juvenileFeatures; + this.compoJuvenile = compoJuvenile; + + } + + /** + * compute the nutrient fluxes for a single fish (in the super individual) + * that dies before spawning + * @param fish + */ + public Map<String,Double> computeNutrientsInputForDeadFish(DiadromousFish fish, ArrayList<String> nutrientsOfInterest) { + + Map<String,Double> nutrientsInput = new Hashtable<String, Double>(); // On créer ici une Map, classe mère des hashtable (Homme = classe mere ie Map//Jules = hashtable) + for (String nutrient : nutrientsOfInterest) { + + if (fish.getStage()== Stage.MATURE) { + double totalWeightPre = this.getWeight(fish,SpawningPosition.PRE); + double carcass = totalWeightPre + * compoCarcassPreSpawning.get(fish.getGender()).get(nutrient); + double excretion = totalWeightPre + * residenceTime + * excretionRate.get(nutrient) ; + double nutrientImport = carcass + excretion; + + nutrientsInput.put(nutrient, nutrientImport); + } + else { + nutrientsInput.put(nutrient, 0.); + } + } + + //TODO Multiply by fish amount + return nutrientsInput; + } + + public Map<String,Double> computeNutrientsInputForDeadFish(DiadromousFish fish) { + + return computeNutrientsInputForDeadFish(fish,this.nutrientsOfInterest); + } + + /** + * compute the nutrient fluxes for a single fish (in the super individual) + * that survives after spawning + * Map: model output = element of interest ie string + double ie the quantification of this fluxes. + * @return nutrientsInput + */ + public Map<String,Double>computeNutrientsInputForSurvivalAfterSpawning(DiadromousFish fish, ArrayList<String> nutrientsOfInterest) { + + Map<String,Double> nutrientsInput = new Hashtable<String,Double>(); + for (String nutrient: nutrientsOfInterest) { + if (fish.getStage()==Stage.MATURE) { + double totalWeightPre = this.getWeight(fish,SpawningPosition.PRE); + double gametes = this.getGonadWeight(fish, SpawningPosition.PRE) - this.getGonadWeight(fish, SpawningPosition.POST) + * compoGametes.get(fish.getGender()).get(nutrient); + double excretion = totalWeightPre + * residenceTime + * excretionRate.get(nutrient); + double nutrientImport = gametes + excretion; + + nutrientsInput.put(nutrient, nutrientImport); + } + else { + nutrientsInput.put(nutrient,0.); + } + } + return nutrientsInput; + } + + public Map<String,Double>computeNutrientsInputForSurvivalAfterSpawning(DiadromousFish fish) { + + return computeNutrientsInputForSurvivalAfterSpawning(fish, this.nutrientsOfInterest); + } + + + public Map<String,Double> computeNutrientsExportForJuveniles(DiadromousFish juvenileFish, ArrayList<String>nutrientsOfInterest) { + Map<String,Double> nutrientsExport = new Hashtable<String,Double>(); + for(String nutrient: nutrientsOfInterest) { + if(juvenileFish.getStage()==Stage.IMMATURE) { + + double JuvenileMass = this.getWeight(juvenileFish); + nutrientsExport.put(nutrient, JuvenileMass * compoJuvenile.get(nutrient)); + } + } + + return nutrientsExport; + } + + public Map<String,Double> computeNutrientsExportForJuveniles(DiadromousFish juvenileFish){ + return computeNutrientsExportForJuveniles(juvenileFish, this.nutrientsOfInterest); + + } + + /** + * Compute the weight for a fish with length (cm) + * @param fish + * @return weight (g) + */ + public double getWeight (DiadromousFish fish, SpawningPosition spawningPosition) { + + double weight = 0.; + if (fish.getStage()==Stage.IMMATURE) + weight = juvenileFeatures.get("aLW") * Math.pow(fish.getLength(),juvenileFeatures.get("bLW")); + else //Stage.MATURE + weight = fishFeaturesPreSpawning.get(fish.getGender()).get("aLW") * Math.pow(fish.getLength(), fishFeaturesPreSpawning.get(fish.getGender()).get("bLW") ); + + return weight; + } + + /** + * Compute the weight for a fish with length (cm) + * @param fish + * @return weight (g) + */ + public double getWeight (DiadromousFish fish) { + + return getWeight (fish, SpawningPosition.PRE); + } + + + /** + * Compute the gonad weight for a fish with length (cm) to compute the gamete emission (g). + * @param fish + * @return weight (g) + */ + public double getGonadWeight (DiadromousFish fish, SpawningPosition spawningPosition) { + + double gonadWeight = 0.; + if (fish.getStage()==Stage.MATURE); + if (spawningPosition == SpawningPosition.PRE) + gonadWeight = Math.exp(fishFeaturesPreSpawning.get(fish.getGender()).get("aLW_Gonad") + + fishFeaturesPreSpawning.get(fish.getGender()).get("bLW_Gonad") * Math.log(fish.getLength())); + else + gonadWeight = Math.exp(fishFeaturesPostSpawning.get(fish.getGender()).get("aLW_Gonad") + + fishFeaturesPostSpawning.get(fish.getGender()).get("bLW_Gonad") * Math.log(fish.getLength())); + + return gonadWeight; + } + + /** + * Compute the gonad weight for a fish with length (cm) to compute the gamete emission (g). + * @param fish + * @return weight (g) + */ + public double getGonadWeight (DiadromousFish fish) { + + return getGonadWeight (fish, SpawningPosition.PRE); + } + + + + + public ArrayList<String> getNutrientsOfInterest() { + return nutrientsOfInterest; + } + + /** + * @param args + */ + /** + * @param args + */ + /** + * @param args + */ + public static void main(String[] args) { + + + double aResidenceTime =30; + + System.out.println("aResidenceTime: " + aResidenceTime); // + + + Map <String, Double> anExcretionRate = new Hashtable <String, Double>(); + anExcretionRate.put("N", 24.71E-6); //values from Barber et al, Alewifes in ug/g wet mass/hour : convertit en g + anExcretionRate.put("P", 2.17E-6); //values from Barber et al, Alewifes in ug/g wet mass/hour: convertit en g + + System.out.println("anExcretionRate: " + anExcretionRate.toString()); // + + /* + * A feature pre spawning + */ + Map<Gender, Map<String, Double>> aFeaturePreSpawning = new Hashtable<DiadromousFish.Gender, Map<String,Double>>(); + + /* + * For females + */ + Map<String,Double> aFeature = new Hashtable<String,Double>(); + + aFeature.put("bLW", 3.3429); //From Taverny + aFeature.put("aLW", 1.2102E-6 * Math.pow(10., aFeature.get("bLW"))); //weight size relationship -- Conversion des g/mm en g.cm (from Taverny, 1991) + aFeature.put("bLW_Gonad", 2.6729); // issu de la relation taille - poids des gonades Bruch + aFeature.put("aLW_Gonad", -5.2425); // issu de la relation taille - poids des gonades Bruch + + aFeaturePreSpawning.put(Gender.FEMALE, aFeature); + + /* + * For males + */ + aFeature = new Hashtable<String,Double>(); + aFeature.put("bLW", 3.2252); + aFeature.put("aLW", 2.4386E-6 * Math.pow(10., aFeature.get("bLW"))); //weight size relationship from Taverny -- Conversion des g/mm en g.cm (from Taverny, 1991) + aFeature.put("bLW_Gonad", 3.3838); + aFeature.put("aLW_Gonad", -8.8744); + aFeaturePreSpawning.put(Gender.MALE,aFeature); + + System.out.println("aFeaturePreSpawning: " + aFeaturePreSpawning.toString()); // + + /* + * a Feature post Spawning + */ + Map<Gender, Map<String, Double>> aFeaturePostSpawning = new Hashtable<DiadromousFish.Gender, Map<String,Double>>(); + + /* + * For females + */ + aFeature = new Hashtable<String,Double>(); + + aFeature.put("aLW_Gonad", -6.6234); // issu de la relation taille - poids des gonades Bruch + aFeature.put("bLW_Gonad", 2.8545); // issu de la relation taille - poids des gonades Bruch + + aFeaturePostSpawning.put(Gender.FEMALE, aFeature); + + /* + * For males + */ + aFeature = new Hashtable<String,Double>(); + + aFeature.put("aLW_Gonad", -11.285); // issu de la relation taille - poids des gonades Bruch + aFeature.put("bLW_Gonad", 3.8331); // issu de la relation taille - poids des gonades Bruch + + aFeaturePostSpawning.put(Gender.MALE,aFeature); + + System.out.println("aFeaturePostSpawning: " + aFeaturePostSpawning.toString()); + + // carcass composition for fish before spawning + Map<Gender, Map<String, Double>> aCompoCarcassPreSpawning = new Hashtable<DiadromousFish.Gender,Map<String,Double>>(); + Map<String,Double> aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 2.958 / 100.); //On remplit une collection avec un put. Values from Haskell (2018) Alosa sapidissima (%) + aCompo.put("P", 0.673 / 100.); //Values from Haskell (2018) Alosa sapidissima (%) + aCompoCarcassPreSpawning.put(Gender.FEMALE,aCompo); + + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 2.941 / 100.); //Values from Haskell (2018) Alosa sapidissima (%) + aCompo.put("P", 0.666 / 100.);// Values from Haskell (2018) Alosa sapidissima (%) + aCompoCarcassPreSpawning.put(Gender.MALE,aCompo); + + System.out.println("aCompoCarcassPreSpawning: " + aCompoCarcassPreSpawning.toString()); // + + + // Gametes composition approximated by the difference between gonads weight before and after spawning. + Map<Gender, Map<String, Double>> aCompoGametes = new Hashtable<DiadromousFish.Gender,Map<String,Double>>(); + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 3.242 / 100.); //On remplit une collection avec un put. From Haskel et al, 2018. + aCompo.put("P", 0.320 / 100.); // Haskel = %P, N, ici ratio donc divise par 100 + aCompoGametes.put(Gender.FEMALE,aCompo); + + aCompo = new Hashtable<String,Double>(); + aCompo.put("N", 3.250 / 100.); // Approximée par la compo des gonades + aCompo.put("P", 0.724 / 100.); + aCompoGametes.put(Gender.MALE,aCompo); + + System.out.println("aCompoGametes:" + aCompoGametes.toString()); // + + // features for juveniles + + Map<String,Double> aJuvenileFeatures = new Hashtable<String, Double>(); + aJuvenileFeatures.put("bLW",3.0306); + aJuvenileFeatures.put("aLW",Math.exp(-11.942) * Math.pow(10., aJuvenileFeatures.get("bLW"))); + + System.out.println("aJuvenileFeatures: " + aJuvenileFeatures.toString()); + + // carcass composition for juveniles fish + Map<String, Double> aCompoJuveniles = new Hashtable<String,Double>(); + aCompoJuveniles.put("N", 2.803 / 100.); //On remplit une collection avec un put. %N in wet weight (Haskell et al, 2017) on Alosa sapidissima + aCompoJuveniles.put("P", 0.887 / 100.); //%P in wet weight (from Haskell et al, 2017) on Alosa sapidissima + + System.out.println("aCompoJuveniles: " + aCompoJuveniles.toString()); + + ArrayList <String> nutrientsOfInterest= new ArrayList <String>(); + nutrientsOfInterest.add("N"); + nutrientsOfInterest.add("P"); + + System.out.println("nutrientsOfInterest: " + nutrientsOfInterest); + + + NutrientRoutineEssay fn = new NutrientRoutineEssay(nutrientsOfInterest,aResidenceTime, anExcretionRate, aFeaturePreSpawning, aFeaturePostSpawning, + aCompoCarcassPreSpawning, aCompoGametes, aJuvenileFeatures, aCompoJuveniles); + + SeaBasin basin = new SeaBasin(0,"Bidon",10.,12., 14.,12.); //il faut aller dans "SeaBasin" dans "environement et regarder comment est construit le constructeur. Il lui faut ici un rang, un nom de bassin versant, et des température pour chaque saison + Pilot pilot = new Pilot (); + DiadromousFish fishFemale = new DiadromousFish (pilot, basin, 52., 1L, Gender.FEMALE); //Idem ici, on regarde comment est construit DiadromousFih et on lui donne les valeur de ce qu'il nous demande. + fishFemale.setStage(Stage.MATURE); + DiadromousFish fishMale = new DiadromousFish (pilot, basin, 47., 1L, Gender.MALE); //Idem ici, on regarde comment est construit DiadromousFih et on lui donne les valeur de ce qu'il nous demande. + fishMale.setStage(Stage.MATURE); + DiadromousFish juvenileFish = new DiadromousFish(pilot,basin,7.0,1L,Gender.UNDIFFERENCIED); + juvenileFish.setStage(Stage.IMMATURE); + + System.out.println(); // affiche une ligne blanche + System.out.println(fishFemale.getGender() + ": " + fishFemale.getLength() + " cm " + fn.getWeight(fishFemale, SpawningPosition.PRE)+ " g " + fn.getWeight(fishFemale, SpawningPosition.POST)); + System.out.println("\tNutrients Fluxes for dead fish " + fn.computeNutrientsInputForDeadFish(fishFemale).toString()); + System.out.println("\tNutrients Fluxes for survival " + fn.computeNutrientsInputForSurvivalAfterSpawning(fishFemale).toString()); + + System.out.println(fishMale.getGender() + ": " + fishMale.getLength() + " cm " + fn.getWeight(fishMale, SpawningPosition.PRE)+ " g " + fn.getWeight(fishMale, SpawningPosition.POST)); + System.out.println("\tNutrients Fluxes for dead fish " + fn.computeNutrientsInputForDeadFish(fishMale).toString()); + System.out.println("\tNutrients Fluxes for survival " + fn.computeNutrientsInputForSurvivalAfterSpawning(fishMale).toString()); + + System.out.println(juvenileFish.getStage() + ": " + juvenileFish.getLength() + " cm " + fn.getWeight(juvenileFish)+ " g "); + System.out.println("\tNutrients Fluxes for juveniles " + fn.computeNutrientsExportForJuveniles(juvenileFish).toString()); + + + /* Create XML file + * + */ + System.out.println((new XStream(new DomDriver())).toXML(fn)); + + } +} + + diff --git a/src/main/java/species/PopulateBasinNetwork.java b/src/main/java/species/PopulateBasinNetwork.java index 68631510d0a9acf80f67206f587c1a384fca0323..b5ac2250ec00a823386a0f238ed00242034eafc1 100644 --- a/src/main/java/species/PopulateBasinNetwork.java +++ b/src/main/java/species/PopulateBasinNetwork.java @@ -5,6 +5,8 @@ import com.thoughtworks.xstream.io.xml.DomDriver; import environment.Basin; import fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess; +import species.DiadromousFish.Gender; + import org.openide.util.lookup.ServiceProvider; @ServiceProvider(service = AquaNismsGroupProcess.class) @@ -19,9 +21,16 @@ public class PopulateBasinNetwork extends AquaNismsGroupProcess<DiadromousFish, @Override public void doProcess(DiadromousFishGroup group) { + + int nbFemaleSIPerBasin = nbSIPerBasin / 2; + int nbMaleSIPerBasin = nbSIPerBasin - nbFemaleSIPerBasin; + for (Basin basin : group.getEnvironment().getRiverBasins()){ - for (int i=0; i < nbSIPerBasin; i++){ - group.addAquaNism(new DiadromousFish(group.getPilot(), basin, initialLength, nbFishPerSI)); + for (int i=0; i < nbFemaleSIPerBasin; i++){ + group.addAquaNism(new DiadromousFish(group.getPilot(), basin, initialLength, nbFishPerSI, Gender.FEMALE)); + } + for (int i=0; i < nbMaleSIPerBasin; i++){ + group.addAquaNism(new DiadromousFish(group.getPilot(), basin, initialLength, nbFishPerSI, Gender.MALE)); } } } diff --git a/src/main/java/species/PopulateBasinNetworkWithANorthLimit.java b/src/main/java/species/PopulateBasinNetworkWithANorthLimit.java index 1c1b972a3f04fb023f1d9a11982b0fd964e53edd..55aa6eb3d72f04a54c82b00258e8661c1f7fceb3 100644 --- a/src/main/java/species/PopulateBasinNetworkWithANorthLimit.java +++ b/src/main/java/species/PopulateBasinNetworkWithANorthLimit.java @@ -24,6 +24,7 @@ import com.thoughtworks.xstream.io.xml.DomDriver; import environment.RiverBasin; import fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess; +import species.DiadromousFish.Gender; /** * @@ -43,10 +44,15 @@ public class PopulateBasinNetworkWithANorthLimit extends AquaNismsGroupProcess<D */ @Override public void doProcess(DiadromousFishGroup group) { + int nbFemaleSIPerBasin = nbSIPerBasin / 2; + int nbMaleSIPerBasin = nbSIPerBasin - nbFemaleSIPerBasin; for (RiverBasin basin : group.getEnvironment().getRiverBasins()){ if (basin.getLatitude()<=northLimitLatitude){ - for (int i=0; i < nbSIPerBasin; i++){ - group.addAquaNism(new DiadromousFish(group.getPilot(), basin, initialLength, nbFishPerSI)); + for (int i=0; i < nbFemaleSIPerBasin; i++){ + group.addAquaNism(new DiadromousFish(group.getPilot(), basin, initialLength, nbFishPerSI, Gender.FEMALE)); + } + for (int i=0; i < nbMaleSIPerBasin; i++){ + group.addAquaNism(new DiadromousFish(group.getPilot(), basin, initialLength, nbFishPerSI, Gender.MALE)); } } } diff --git a/src/main/java/species/PopulateWithASinglePopulation.java b/src/main/java/species/PopulateWithASinglePopulation.java index 5278c926286487d2c3b45f42bbf7278adabc342f..988fba6491a1467cc8c0e5d9e5cbdd247f20d14f 100644 --- a/src/main/java/species/PopulateWithASinglePopulation.java +++ b/src/main/java/species/PopulateWithASinglePopulation.java @@ -5,13 +5,15 @@ import com.thoughtworks.xstream.io.xml.DomDriver; import environment.Basin; import fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess; +import species.DiadromousFish.Gender; + import org.openide.util.lookup.ServiceProvider; @ServiceProvider(service = AquaNismsGroupProcess.class) public class PopulateWithASinglePopulation extends AquaNismsGroupProcess<DiadromousFish, DiadromousFishGroup> { private int nbSIPerBasin=100; - private long nbIndPerSI = 10; + private long nbFishPerSI = 10; private int bassinInd =12; private double initialLength =2.; @@ -20,10 +22,14 @@ public class PopulateWithASinglePopulation extends AquaNismsGroupProcess<Diadrom @Override public void doProcess(DiadromousFishGroup group) { - + int nbFemaleSIPerBasin = nbSIPerBasin / 2; + int nbMaleSIPerBasin = nbSIPerBasin - nbFemaleSIPerBasin; Basin basin = group.getEnvironment().getRiverBasins()[bassinInd]; - for (int i=0; i < nbSIPerBasin; i++){ - group.addAquaNism(new DiadromousFish(group.getPilot(), basin, initialLength, nbIndPerSI)); + for (int i=0; i < nbFemaleSIPerBasin; i++){ + group.addAquaNism(new DiadromousFish(group.getPilot(), basin, initialLength, nbFishPerSI, Gender.FEMALE)); + } + for (int i=0; i < nbMaleSIPerBasin; i++){ + group.addAquaNism(new DiadromousFish(group.getPilot(), basin, initialLength, nbFishPerSI, Gender.MALE)); } } } diff --git a/src/main/java/species/ReproduceAndSurviveAfterReproduction.java b/src/main/java/species/ReproduceAndSurviveAfterReproduction.java index 9903cfa63d4da333970b41e1aabaa4c872662acf..c6acbc1b74205b18151ad568f373f839f2b6f395 100644 --- a/src/main/java/species/ReproduceAndSurviveAfterReproduction.java +++ b/src/main/java/species/ReproduceAndSurviveAfterReproduction.java @@ -10,6 +10,7 @@ import java.util.TreeMap; import miscellaneous.Duo; import miscellaneous.Miscellaneous; +import species.DiadromousFish.Gender; import org.openide.util.lookup.ServiceProvider; @@ -26,8 +27,8 @@ import fr.cemagref.simaqualife.kernel.processes.AquaNismsGroupProcess; import fr.cemagref.simaqualife.kernel.util.TransientParameters.InitTransientParameters; import fr.cemagref.simaqualife.pilot.Pilot; +@Deprecated @ServiceProvider(service = AquaNismsGroupProcess.class) - public class ReproduceAndSurviveAfterReproduction extends AquaNismsGroupProcess<DiadromousFish, DiadromousFishGroup>{ // for the calibration of the model we use S_etoileGir = 190000; surfGir = 80351; @@ -43,15 +44,12 @@ public class ReproduceAndSurviveAfterReproduction extends AquaNismsGroupProcess< private double delta_t=0.33; // duration of the mortality considered in the reproduction process (ex.: from eggs to juvenile in estuary for alosa alosa = 0.33) private double survOptRep = 0.0017; private double lambda = 0.00041; - private double initialLength = 2.; private double sigmaRecruitment = 0.3; private double survivalRateAfterReproduction = 0.1; private double maxNumberOfSuperIndividualPerReproduction = 50.; - private transient NormalGen genNormal; - // private transient UniformGen genUniform; public static void main(String[] args) { System.out.println((new XStream(new DomDriver())) .toXML(new ReproduceAndSurviveAfterReproduction())); } @@ -92,14 +90,15 @@ public class ReproduceAndSurviveAfterReproduction extends AquaNismsGroupProcess< // origins of spawner during this reproduction Map<String, Long> spawnerOriginsDuringReproduction = new HashMap<String, Long>(group.getEnvironment().getRiverBasinNames().length); for (String basinName : group.getEnvironment().getRiverBasinNames()){ - spawnerOriginsDuringReproduction.put(basinName, (long) 0); + spawnerOriginsDuringReproduction.put(basinName, 0L); } // System.out.println("REPRODUCTION in "+riverBasin.getName()+" : FISH FROM "); List<DiadromousFish> fishInBasin = riverBasin.getFishs(group); if (fishInBasin != null){ - //Calcul of b and c in stock-recruitment relationships + + // effective temperature for reproduction (priority to the ANG value) double tempEffectRep; if (group.getTempMinRep() == Double.NaN){ tempEffectRep = Miscellaneous.temperatureEffect(riverBasin.getCurrentTemperature(group.getPilot()), tempMinRep, tempOptRep, tempMaxRep); @@ -108,36 +107,36 @@ public class ReproduceAndSurviveAfterReproduction extends AquaNismsGroupProcess< tempEffectRep = Miscellaneous.temperatureEffect(riverBasin.getCurrentTemperature(group.getPilot()), group.getTempMinRep(), tempOptRep, tempMaxRep); } - if (tempEffectRep == 0.) - b=0; - else - b = - Math.log(survOptRep * tempEffectRep) / delta_t; - + // Compute the prelimenary parameters b and c for the stock-recruitment relationship + b = (tempEffectRep == 0.) ? 0. : - Math.log(survOptRep * tempEffectRep) / delta_t; c = lambda/riverBasin.getAccessibleSurface(); - // Calcul of alpha and beta of the basin + // Compute alpha and beta parameters of the the stock-recruitment relationship alpha = (b * Math.exp(- b * delta_t))/(c * (1 - Math.exp(- b * delta_t))); beta = b / (a * c * (1 - Math.exp(- b * delta_t))); - //System.out.println(a+ ", " +b + ", " + c + ", " + delta_t + "= "+ alpha); + + // keep the last value of alpha (productive capacities) riverBasin.getLastProdCapacities().push(alpha); - // Calcul of the amount per superIndividual + // Compute the amount per superIndividual amountPerSuperIndividual = alpha / maxNumberOfSuperIndividualPerReproduction; - // Calcul of Setoile, S95 and S50 - Setoile = eta * riverBasin.getAccessibleSurface(); - S95 = Setoile; + // Compute the Allee effect parameters S95 and S50 + S95 = eta * riverBasin.getAccessibleSurface(); // corresponds to S* in the rougier publication S50 = S95 / ratioS95_S50; - // compute the number of spawners and keep the origines of the spawners + // initilisation of the stock recruitment relationship + //stockRecruitmentRelationship.init(alpha, beta, S50, S95); + // age of autochnonous spawnser //Map<Integer, Long> ageOfNativeSpawners = new TreeMap<Integer, Long>(); + // compute the number of spawners and keep the origine of the spawners for( DiadromousFish fish : fishInBasin){ if( fish.isMature()){ if (fish.getNumberOfReproduction() < 1) { - numberOfSpawnerForFirstTime++; + numberOfSpawnerForFirstTime++; //ASK individual or super-individual ? spawnersForFirstTimeAgesSum += fish.getAge(); } numberOfGenitors += fish.getAmount() ; @@ -146,31 +145,32 @@ public class ReproduceAndSurviveAfterReproduction extends AquaNismsGroupProcess< String basinName = fish.getBirthBasin().getName(); spawnerOriginsDuringReproduction.put(basinName, spawnerOriginsDuringReproduction.get(basinName) + fish.getAmount() ); - // number of autochtone and age of autochnone - if (riverBasin == fish.getBirthBasin()){ + // number of autochtonous fish per age + /*if (riverBasin == fish.getBirthBasin()){ numberOfAutochtones += fish.getAmount(); - /*Integer age = (int) Math.floor(fish.getAge()); + Integer age = (int) Math.floor(fish.getAge()); if (ageOfNativeSpawners.containsKey(age)) ageOfNativeSpawners.put(age, ageOfNativeSpawners.get(age)+fish.getAmount()); else - ageOfNativeSpawners.put(age, fish.getAmount());*/ - } + ageOfNativeSpawners.put(age, fish.getAmount()); + } */ - //System.out.println("l'âge du poisson est :" + fish.getAge() + " et la saison est :" + Time.getSeason()); - // Survive After Reproduction + // increment number of reproduction (for possible iteroparty) fish.incNumberOfReproduction(); // survival after reproduction (semelparity or iteroparity) of SI (change the amount of the SI) survivalAmount = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), survivalRateAfterReproduction); if (survivalAmount > 0) fish.setAmount(survivalAmount); + else deadFish.add(fish); } } + System.out.println(" numberOfGenitors: "+ numberOfGenitors); /* - // calcul de la mortalité associée aux géniteurs autochtones + // calcul de la mortalit� associ�e aux g�niteurs autochtones if (numberOfGenitors > 0.) { System.out.println(riverBasin.getName().toUpperCase()); System.out.println("Spawners"); @@ -209,41 +209,45 @@ public class ReproduceAndSurviveAfterReproduction extends AquaNismsGroupProcess< double meanNumberOfRecruit = Math.round((alpha * numberOfGenitors * (1 / (1 + Math.exp(- Math.log(19)*((numberOfGenitors - S50) / (S95 - S50)))))) / (beta + numberOfGenitors * (1 / (1 + Math.exp(- Math.log(19)*((numberOfGenitors - S50) / (S95 - S50))))))); - muRecruitment = Math.log(meanNumberOfRecruit) - (Math.pow(sigmaRecruitment,2))/2; + // lognormal random draw + muRecruitment = Math.log(meanNumberOfRecruit) - (Math.pow(sigmaRecruitment,2))/2; long numberOfRecruit = Math.round(Math.exp(genNormal.nextDouble()*sigmaRecruitment + muRecruitment)); + // keep last % of autochtone riverBasin.getLastPercentagesOfAutochtones().push(numberOfAutochtones * 100 / numberOfGenitors); + // keep the number of spawners for the firt time in the basin if (numberOfSpawnerForFirstTime>0){ - riverBasin.getSpawnersForFirstTimeMeanAges().push(spawnersForFirstTimeAgesSum/numberOfSpawnerForFirstTime); + riverBasin.getSpawnersForFirstTimeMeanAges(Gender.FEMALE).push(spawnersForFirstTimeAgesSum/numberOfSpawnerForFirstTime); }else{ - riverBasin.getSpawnersForFirstTimeMeanAges().push(0.); + riverBasin.getSpawnersForFirstTimeMeanAges(Gender.FEMALE).push(0.); } //System.out.println("nb spawners in basin " + riverBasin.getName() + " : " + numberOfGenitors); //System.out.println("nb recruit in basin " + riverBasin.getName() + " : " + numberOfRecruit); // Creation of new superFish - if(numberOfRecruit > 0){ - // stock the first year when recruitment is non nul - if(riverBasin.getYearOfFirstNonNulRep() == 0){ - riverBasin.setYearOfFirstNonNulRep(Time.getYear(group.getPilot())); - } - + if (numberOfRecruit > 0){ + + // features of the super individuals int numberOfsuperIndividual = Math.max(1, (int) Math.round(numberOfRecruit / amountPerSuperIndividual)); long effectiveAmount = numberOfRecruit / numberOfsuperIndividual; - - // System.out.println(numberOfRecruit + " / " + amountPerSuperIndividual +" = " +numberOfsuperIndividual); - //System.out.println(numberOfRecruit + " / " + numberOfsuperIndividual +" = " +effectiveAmount); + for (int i=0; i<numberOfsuperIndividual; i++){ - group.addAquaNism(new DiadromousFish(group.getPilot(), riverBasin, initialLength, effectiveAmount)); + group.addAquaNism(new DiadromousFish(group.getPilot(), riverBasin, group.getLengthAtHatching(), effectiveAmount, Gender.UNDIFFERENCIED)); } + + // stock the first year when recruitment is non nul + if (riverBasin.getYearOfFirstNonNulRep() == 0){ + riverBasin.setYearOfFirstNonNulRep(Time.getYear(group.getPilot())); + } riverBasin.getLastRecruitmentExpectations().push(Math.round(meanNumberOfRecruit)); - riverBasin.getLastRecruitments().push(numberOfsuperIndividual * effectiveAmount); // on remplit la pile qui permet de stocker un nombre fixé de derniers recrutement + riverBasin.getLastRecruitments().push(numberOfsuperIndividual * effectiveAmount); // on remplit la pile qui permet de stocker un nombre fix� de derniers recrutement riverBasin.getLastRecsOverProdCaps().push(((double) riverBasin.getLastRecruitments().getLastItem())/riverBasin.getLastProdCapacities().getLastItem()); + if (numberOfAutochtones > 0){ riverBasin.getNumberOfNonNulRecruitmentForFinalProbOfPres().push(1.0); riverBasin.getNumberOfNonNulRecruitmentDuringLastYears().push(1.0); @@ -289,7 +293,7 @@ public class ReproduceAndSurviveAfterReproduction extends AquaNismsGroupProcess< riverBasin.getSpawnerOrigins().push(spawnerOriginsDuringReproduction); //System.out.println(" AFTER " +riverBasin.getSpawnerOrigins().keySet()); } - // on met à jour les observeurs + // on met � jour les observeurs for (RiverBasin riverBasin : group.getEnvironment().getRiverBasins()){ riverBasin.getCobservable().fireChanges(riverBasin, pilot.getCurrentTime()); } diff --git a/src/main/java/species/ReproduceAndSurviveAfterReproductionWithDiagnose.java b/src/main/java/species/ReproduceAndSurviveAfterReproductionWithDiagnose.java index 530e581296c1df8c95f57c3b7094c2ca333e100e..4f7040ec5f1a14d087265ed7144e0281906f6c12 100644 --- a/src/main/java/species/ReproduceAndSurviveAfterReproductionWithDiagnose.java +++ b/src/main/java/species/ReproduceAndSurviveAfterReproductionWithDiagnose.java @@ -1,8 +1,12 @@ package species; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.ObjectInputStream.GetField; import java.util.ArrayList; import java.util.HashMap; +import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -11,6 +15,7 @@ import java.util.TreeMap; import miscellaneous.Duo; import miscellaneous.Miscellaneous; import miscellaneous.Trio; +import species.DiadromousFish.Gender; import org.apache.commons.math3.analysis.UnivariateFunction; import org.apache.commons.math3.optim.MaxEval; @@ -55,10 +60,20 @@ public class ReproduceAndSurviveAfterReproductionWithDiagnose extends AquaNismsG private double sigmaRecruitment = 0.3; private double survivalRateAfterReproduction = 0.1; private double maxNumberOfSuperIndividualPerReproduction = 50.; - + private boolean withDiagnose = true; + private double proportionOfFemaleAtBirth =0.5; + private boolean displayFluxesOnConsole = true; private transient NormalGen genNormal; private transient MortalityFunction mortalityFunction; + + private enum fluxOrigin {AUTOCHTONOUS, ALLOCHTONOUS}; + /** + * relationship between + * recruitment in number of juvenile on spawning grounds + * stock in number of FEMALES + * @unit + */ private transient StockRecruitmentRelationship stockRecruitmentRelationship; // private transient UniformGen genUniform; @@ -75,6 +90,14 @@ public class ReproduceAndSurviveAfterReproductionWithDiagnose extends AquaNismsG stockRecruitmentRelationship= new StockRecruitmentRelationship(); } + + /** + * @return the tempMinRep + */ + public double getTempMinRep() { + return tempMinRep; + } + @Override public void doProcess(DiadromousFishGroup group) { @@ -82,11 +105,22 @@ public class ReproduceAndSurviveAfterReproductionWithDiagnose extends AquaNismsG List<DiadromousFish> deadFish = new ArrayList<DiadromousFish>(); for(RiverBasin riverBasin : group.getEnvironment().getRiverBasins()){ - double b, c, alpha, beta, amountPerSuperIndividual , Setoile, S95, S50 ; - double numberOfGenitors = 0.; + + // before the party !!!! + double fluxBefore =riverBasin.getSpawnerNumber(); + + double b, c, alpha, beta, amountPerSuperIndividual , S95, S50 ; + double numberOfFemaleSpawners = 0.; + double numberOfMaleSpawners = 0.; double numberOfAutochtones = 0.; - double numberOfSpawnerForFirstTime = 0.; - double spawnersForFirstTimeAgesSum = 0.; + + double numberOfFemaleSpawnerForFirstTime = 0.; + double femaleSpawnersForFirstTimeAgesSum = 0.; + double femaleSpawnersForFirstTimeLengthsSum = 0.; + double numberOfMaleSpawnerForFirstTime = 0.; + double maleSpawnersForFirstTimeAgesSum = 0.; + double maleSpawnersForFirstTimeLengthsSum = 0.; + long survivalAmount; double muRecruitment = 0.; //double weightOfGenitors = 0.; @@ -94,194 +128,335 @@ public class ReproduceAndSurviveAfterReproductionWithDiagnose extends AquaNismsG // origins of spawner during this reproduction Map<String, Long> spawnerOriginsDuringReproduction = new HashMap<String, Long>(group.getEnvironment().getRiverBasinNames().length); for (String basinName : group.getEnvironment().getRiverBasinNames()){ - spawnerOriginsDuringReproduction.put(basinName, (long) 0); + spawnerOriginsDuringReproduction.put(basinName, 0L); } List<DiadromousFish> fishInBasin = riverBasin.getFishs(group); if (fishInBasin != null){ - //Calcul of b and c in stock-recruitment relationships + //Initiate the total fluxes for this basin + Map<fluxOrigin, Map<String, Double>> totalInputFluxes = new Hashtable<fluxOrigin, Map <String, Double>>(); //On cr�er la Map pour stocker les flux + totalInputFluxes.put(fluxOrigin.AUTOCHTONOUS, new Hashtable < String, Double>()); + totalInputFluxes.put(fluxOrigin.ALLOCHTONOUS, new Hashtable < String, Double>()); + for (fluxOrigin origin: totalInputFluxes.keySet()) { + for (String nutrient : group.getNutrientRoutine().getNutrientsOfInterest()) { + totalInputFluxes.get(origin).put(nutrient, 0.); // ON MET A JOUR NOTRE map + } + totalInputFluxes.get(origin).put("biomass",0.); + } + + + // -------------------------------------------------------------------------------------------------- + // definition of the stock recruiment relationship + // -------------------------------------------------------------------------------------------------- + + // effective temperature for reproduction (priority to the ANG value) double tempEffectRep; - if (group.getTempMinRep() == Double.NaN){ + if (Double.isNaN(group.getTempMinRep())){ tempEffectRep = Miscellaneous.temperatureEffect(riverBasin.getCurrentTemperature(group.getPilot()), tempMinRep, tempOptRep, tempMaxRep); } else { tempEffectRep = Miscellaneous.temperatureEffect(riverBasin.getCurrentTemperature(group.getPilot()), group.getTempMinRep(), tempOptRep, tempMaxRep); } - // Calcul of alpha and beta of the basin + // Compute the prelimenary parameters b and c for the stock-recruitment relationship + b = (tempEffectRep == 0.) ? 0. : - Math.log(survOptRep * tempEffectRep) / delta_t; c = lambda/riverBasin.getAccessibleSurface(); - if (tempEffectRep == 0.){ - b=0; - alpha=0.; - beta=0.; - } - else { - b = - Math.log(survOptRep * tempEffectRep) / delta_t; - alpha = (b * Math.exp(- b * delta_t))/(c * (1 - Math.exp(- b * delta_t))); - beta = b / (a * c * (1 - Math.exp(- b * delta_t))); - } + // Compute alpha and beta parameters of the the stock-recruitment relationship + alpha = (b * Math.exp(- b * delta_t))/(c * (1 - Math.exp(- b * delta_t))); + beta = b / (a * c * (1 - Math.exp(- b * delta_t))); //System.out.println(a+ ", " +b + ", " + c + ", " + delta_t + "= "+ alpha); + + // keep the last value of alpha (productive capacities) riverBasin.getLastProdCapacities().push(alpha); - // Calcul of the amount per superIndividual + // Compute the amount per superIndividual amountPerSuperIndividual = alpha / maxNumberOfSuperIndividualPerReproduction; - // Calcul of Setoile, S95 and S50 - Setoile = eta * riverBasin.getAccessibleSurface(); - S95 = Setoile; + // Compute the Allee effect parameters S95 and S50 + S95 = eta * riverBasin.getAccessibleSurface(); // corresponds to S* in the rougier publication S50 = S95 / ratioS95_S50; - // initilisation de la relation stock recruitment + // initilisation of the stock recruitment relationship stockRecruitmentRelationship.init(alpha, beta, S50, S95); - // calcul de Zcrash - //Double Zcrash = Math.log(alpha*(d-1)/(d*beta*Math.pow(d-1, 1/d))); + // -------------------------------------------------------------------------------------------------- + // calulation of the spawner number + // -------------------------------------------------------------------------------------------------- + // age of autochnonous spawnser Map<Integer, Long> ageOfNativeSpawners = new TreeMap<Integer, Long>(); - // compute the number of spawners and keep the origines of the spawners - for( DiadromousFish fish : fishInBasin){ - if( fish.isMature()){ - if (fish.getNumberOfReproduction() < 1) { - numberOfSpawnerForFirstTime++; - spawnersForFirstTimeAgesSum += fish.getAge(); + // compute the number of female spawners and keep the origine of the female spawners + for (DiadromousFish fish : fishInBasin){ + + if (fish.isMature()) { + + // number of spawners per gender + if (fish.getGender() == Gender.FEMALE) { + //System.out.println(fish.getAge() + " -> "+ fish.getLength() + " ("+fish.getStage()+")"); + numberOfFemaleSpawners += fish.getAmount() ; // on ajoute a chaque fois le fish.getAmount (CcumSum) + if (fish.getNumberOfReproduction() < 1) { + numberOfFemaleSpawnerForFirstTime += fish.getAmount(); + femaleSpawnersForFirstTimeAgesSum += fish.getAge() * fish.getAmount(); + femaleSpawnersForFirstTimeLengthsSum += fish.getLength() * fish.getAmount(); + } + } + else if (fish.getGender() == Gender.MALE) { + numberOfMaleSpawners += fish.getAmount() ; // on ajoute a chaque fois le fish.getAmount (CcumSum) + if (fish.getNumberOfReproduction() < 1) { + numberOfMaleSpawnerForFirstTime += fish.getAmount(); + maleSpawnersForFirstTimeAgesSum += fish.getAge() * fish.getAmount(); + maleSpawnersForFirstTimeLengthsSum += fish.getLength() * fish.getAmount(); + } } - numberOfGenitors += fish.getAmount() ; // spawner per origine String basinName = fish.getBirthBasin().getName(); spawnerOriginsDuringReproduction.put(basinName, spawnerOriginsDuringReproduction.get(basinName) + fish.getAmount() ); - // number of autochtone and age of autochnone - if (riverBasin == fish.getBirthBasin()){ + // number of autochtonous fish per age + if (riverBasin == fish.getBirthBasin()){ numberOfAutochtones += fish.getAmount(); - Integer age = (int) Math.floor(fish.getAge()); + Integer age = (int) Math.floor(fish.getAge()); //ASK floor() or ceil() if (ageOfNativeSpawners.containsKey(age)) ageOfNativeSpawners.put(age, ageOfNativeSpawners.get(age)+fish.getAmount()); else ageOfNativeSpawners.put(age, fish.getAmount()); } - //System.out.println("l'âge du poisson est :" + fish.getAge() + " et la saison est :" + Time.getSeason()); - // Survive After Reproduction + // increment number of reproduction (for possible iteroparty) fish.incNumberOfReproduction(); - // survival after reproduction (semelparity or iteroparity) of SI (change the amount of the SI) - survivalAmount = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), survivalRateAfterReproduction); - if (survivalAmount > 0) - fish.setAmount(survivalAmount); + // origin of the spwaner + fluxOrigin spawnerOrigin; + if (fish.getBirthBasin() == riverBasin) + spawnerOrigin = fluxOrigin.AUTOCHTONOUS; else + spawnerOrigin = fluxOrigin.ALLOCHTONOUS; + + // survival after reproduction (semelparity or iteroparity) of SI (change the amount of the SI) + double biomass = 0.; + survivalAmount = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), survivalRateAfterReproduction); + + + // update the amount of fish or kill the fish if survival amount = 0 + // compute nutrient fluxes + if (survivalAmount > 0) {// SUperindividu est encore vivant mais il perd des effectifs + + //Export for fishes survived after spawning (survivalAmount) : excretion + gametes + Map <String, Double> aFluxAfterSurvival = group.getNutrientRoutine().computeNutrientsInputForSurvivalAfterSpawning(fish); + + //Export for fishes that dies after spawning (fish.getAmount - survivalAmount): excretion + gametes + carcasse + Map<String, Double> aFluxForDeadFish = group.getNutrientRoutine().computeNutrientsInputForDeathAfterSpawning(fish); + + for (String nutrient: aFluxAfterSurvival.keySet()) { + //For survival fish + totalInputFluxes.get(spawnerOrigin).put(nutrient,totalInputFluxes.get(spawnerOrigin).get(nutrient) + aFluxAfterSurvival.get(nutrient) * survivalAmount); + + //For dead fish + totalInputFluxes.get(spawnerOrigin).put(nutrient,totalInputFluxes.get(spawnerOrigin).get(nutrient) + aFluxForDeadFish.get(nutrient) * (fish.getAmount() - survivalAmount)); + } + + //compute biomass for dead fish + biomass = group.getNutrientRoutine().getWeight(fish) * (fish.getAmount() - survivalAmount); + totalInputFluxes.get(spawnerOrigin).put("biomass", totalInputFluxes.get(spawnerOrigin).get("biomass") + biomass); + + //update the amount of individual in the super-individual + fish.setAmount(survivalAmount); + } + else { + //Le superindividu est mort !!! deadFish.add(fish); - } - } + //Export for fished died before spawning (fish.getAmount): carcasses + excretion + gametes + Map<String, Double> aFlux = group.getNutrientRoutine().computeNutrientsInputForDeathAfterSpawning(fish); // - // prepare les données pour le calcul de la mortalité associée aux géniteurs autochtones - if (numberOfGenitors > 0.) { - List<Trio<Integer, Long, Long>> mortalityData= new ArrayList<Trio<Integer, Long, Long>>(); - // first age - // second effective of native spwaner - // third effective of corresponding recruitment - for (Integer age : ageOfNativeSpawners.keySet()){ - if (riverBasin.getLastRecruitments().getItemFromLast(age) != null){ - mortalityData.add(new Trio<Integer, Long, Long>(age, - ageOfNativeSpawners.get(age), - riverBasin.getLastRecruitments().getItemFromLast(age))); - } else{ - mortalityData.add(new Trio<Integer, Long, Long>(age, 0L, 0L)); + for (String nutrient: aFlux.keySet()) { + totalInputFluxes.get(spawnerOrigin).put(nutrient,totalInputFluxes.get(spawnerOrigin).get(nutrient) + aFlux.get(nutrient) * fish.getAmount()); //Fish.getAmount - survivalAmount = total fishes died. + } + biomass = group.getNutrientRoutine().getWeight(fish) * (fish.getAmount()); + totalInputFluxes.get(spawnerOrigin).put("biomass", totalInputFluxes.get(spawnerOrigin).get("biomass") + biomass); } } - mortalityFunction.init(mortalityData); - riverBasin.setNativeSpawnerMortality(mortalityFunction.getSigmaZ()); - } - else{ - riverBasin.setNativeSpawnerMortality(Double.NaN); } - - riverBasin.setMortalityCrash(stockRecruitmentRelationship.getSigmaZcrash()); - riverBasin.setStockTrap(stockRecruitmentRelationship.getStockTrap(riverBasin.getNativeSpawnerMortality())); - riverBasin.setLastSpawnerNumber(numberOfGenitors); - - // AFFICHAGE DES RESULTATS - /*System.out.println(riverBasin.getName().toUpperCase()); - //System.out.println("alpha="+alpha+ "\tbeta="+beta+"\tS50="+S50+ "\tS95="+S95); - System.out.println("\tScrash="+stockRecruitmentRelationship.getStockAtZcrash()+ - "\tZcrash="+ stockRecruitmentRelationship.getSigmaZcrash() + - "\tZ="+ riverBasin.getNativeSpawnerMortality()); - System.out.println("\tStrap="+stockRecruitmentRelationship.getStockTrap(riverBasin.getNativeSpawnerMortality())+ - "\tStotal="+numberOfGenitors+"\tSautochthonous="+ - spawnerOriginsDuringReproduction.get(riverBasin.getName())); - - - String diagnose; - if (Double.isNaN(riverBasin.getNativeSpawnerMortality())) - diagnose="noSense"; - else { - double stockTrap=stockRecruitmentRelationship.getStockTrap(riverBasin.getNativeSpawnerMortality()); - if (riverBasin.getNativeSpawnerMortality()>stockRecruitmentRelationship.getSigmaZcrash()) - diagnose="overZcrash"; + // keep the number of female spawner + riverBasin.setLastFemaleSpawnerNumber(numberOfFemaleSpawners); + + // -------------------------------------------------------------------------------------------------- + // Diagnose of the population dynamics in the basin + // -------------------------------------------------------------------------------------------------- + if (withDiagnose == true) { + // calculate and keep the features of the stock recruitment relationships + riverBasin.setMortalityCrash(stockRecruitmentRelationship.getSigmaZcrash()); + + // initialise the mortality function for the autochnous spawners + // use to approximate the mortality of all the spawners to give a proxy of the Allee trap + if (numberOfFemaleSpawners > 0.) { + List<Trio<Integer, Long, Long>> mortalityData= new ArrayList<Trio<Integer, Long, Long>>(); + // first age + // second effective of native spwaner + // third effective of corresponding recruitment + for (Integer age : ageOfNativeSpawners.keySet()){ + if (riverBasin.getLastRecruitments().getItemFromLast(age) != null){ + mortalityData.add(new Trio<Integer, Long, Long>(age, + ageOfNativeSpawners.get(age), + riverBasin.getLastRecruitments().getItemFromLast(age))); + } + else{ + mortalityData.add(new Trio<Integer, Long, Long>(age, 0L, 0L)); + } + } + mortalityFunction.init(mortalityData); + riverBasin.setNativeSpawnerMortality(mortalityFunction.getSigmaZ()); + } else { - if (numberOfGenitors < stockTrap) - diagnose = "inTrapWithStrayers"; + riverBasin.setNativeSpawnerMortality(Double.NaN); + } + + riverBasin.setStockTrap(stockRecruitmentRelationship.getStockTrap(riverBasin.getNativeSpawnerMortality())); + + + System.out.println(riverBasin.getName().toUpperCase()); + //System.out.println("alpha="+alpha+ "\tbeta="+beta+"\tS50="+S50+ "\tS95="+S95); + System.out.println("\tScrash="+stockRecruitmentRelationship.getStockAtZcrash()+ + "\tZcrash="+ stockRecruitmentRelationship.getSigmaZcrash() + + "\tZ="+ riverBasin.getNativeSpawnerMortality()); + System.out.println("\tStrap="+stockRecruitmentRelationship.getStockTrap(riverBasin.getNativeSpawnerMortality())+ + "\tStotal="+numberOfFemaleSpawners+"\tSautochthonous="+ + spawnerOriginsDuringReproduction.get(riverBasin.getName())); + + /* // display effective from each catchment + System.out.print(riverBasin.getName()); + for (String natalBasinName : group.getEnvironment().getRiverBasinNames()){ + System.out.print("\t"+natalBasinName); + } + System.out.println(); + System.out.print(riverBasin.getName()); + for (String natalBasinName : group.getEnvironment().getRiverBasinNames()){ + System.out.print("\t"+spawnerOriginsDuringReproduction.get(natalBasinName)); + } + System.out.println();*/ + + // System.out.println("\t"+ riverBasin.getPopulationStatus()); + + String message; + if (Double.isNaN(riverBasin.getNativeSpawnerMortality())) + message="noSense"; + else { + double stockTrap=stockRecruitmentRelationship.getStockTrap(riverBasin.getNativeSpawnerMortality()); + if (riverBasin.getNativeSpawnerMortality()>stockRecruitmentRelationship.getSigmaZcrash()) + message="overZcrash"; else { - if (spawnerOriginsDuringReproduction.get(riverBasin.getName()) < stockTrap) - diagnose = "inTrapWithOnlyNatives"; - else - diagnose = "sustain"; + if (numberOfFemaleSpawners < stockTrap) + message = "inTrapWithStrayers"; + else { + if (spawnerOriginsDuringReproduction.get(riverBasin.getName()) < stockTrap) + message = "inTrapWithOnlyNatives"; + else + message = "sustain"; + } } } + //System.out.println("\t"+message); } - System.out.println("\t"+diagnose);*/ + // -------------------------------------------------------------------------------------------------- + // Reproduction process (compute the number of recruits) + // need to have at least one female and one male + // use the proportion of female at birth to compute the number of recruits of each gender + // -------------------------------------------------------------------------------------------------- + + if (numberOfFemaleSpawners > 0. && numberOfMaleSpawners >0.) { - // Reproduction process (number of recruits) - if (numberOfGenitors > 0.) { //BH Stock-Recruitment relationship with logistic depensation - double meanNumberOfRecruit = stockRecruitmentRelationship.getRecruitment(numberOfGenitors); - muRecruitment = Math.log(meanNumberOfRecruit) - (Math.pow(sigmaRecruitment,2))/2; + double meanNumberOfRecruit = stockRecruitmentRelationship.getRecruitment(numberOfFemaleSpawners); + // lognormal random draw + muRecruitment = Math.log(meanNumberOfRecruit) - (Math.pow(sigmaRecruitment,2))/2; long numberOfRecruit = Math.round(Math.exp(genNormal.nextDouble()*sigmaRecruitment + muRecruitment)); + long numberOfFemaleRecruit = Math.round(numberOfRecruit * proportionOfFemaleAtBirth); + long numberOfMaleRecruit = numberOfRecruit - numberOfFemaleRecruit; - riverBasin.getLastPercentagesOfAutochtones().push(numberOfAutochtones * 100 / numberOfGenitors); + //System.out.println(group.getPilot().getCurrentTime()+" "+Time.getSeason(group.getPilot())+" "+ riverBasin.getName()+": " + numberOfGenitors + " spwaners \tgive "+ numberOfRecruit + " recruits"); - if (numberOfSpawnerForFirstTime>0){ - riverBasin.getSpawnersForFirstTimeMeanAges().push(spawnersForFirstTimeAgesSum/numberOfSpawnerForFirstTime); - }else{ - riverBasin.getSpawnersForFirstTimeMeanAges().push(0.); + // ---------------------------------------------- + // keep information when reproduction + // keep last % of autochtone + riverBasin.getLastPercentagesOfAutochtones().push(numberOfAutochtones * 100 / numberOfFemaleSpawners); + + // keep the number of spawners for the first time in the basin + if (numberOfFemaleSpawnerForFirstTime>0) { + riverBasin.getSpawnersForFirstTimeMeanAges(Gender.FEMALE).push(femaleSpawnersForFirstTimeAgesSum / numberOfFemaleSpawnerForFirstTime); + riverBasin.getSpawnersForFirstTimeMeanLengths(Gender.FEMALE).push(femaleSpawnersForFirstTimeLengthsSum / numberOfFemaleSpawnerForFirstTime); + } + else { + riverBasin.getSpawnersForFirstTimeMeanAges(Gender.FEMALE).push(0.); + riverBasin.getSpawnersForFirstTimeMeanLengths(Gender.MALE).push(0.); + } + if (numberOfMaleSpawnerForFirstTime>0) { + riverBasin.getSpawnersForFirstTimeMeanAges(Gender.MALE).push(maleSpawnersForFirstTimeAgesSum/numberOfMaleSpawnerForFirstTime); + riverBasin.getSpawnersForFirstTimeMeanLengths(Gender.MALE).push(maleSpawnersForFirstTimeLengthsSum / numberOfMaleSpawnerForFirstTime); + } + else { + riverBasin.getSpawnersForFirstTimeMeanAges(Gender.MALE).push(0.); + riverBasin.getSpawnersForFirstTimeMeanLengths(Gender.MALE).push(0.); } + //System.out.println("nb spawners in basin " + riverBasin.getName() + " : " + numberOfGenitors); //System.out.println("nb recruit in basin " + riverBasin.getName() + " : " + numberOfRecruit); // Creation of new superFish - if(numberOfRecruit > 0){ - // stock the first year when recruitment is non nul - if(riverBasin.getYearOfFirstNonNulRep() == 0){ - riverBasin.setYearOfFirstNonNulRep(Time.getYear(group.getPilot())); + if (numberOfRecruit > 0){ + + long numberOfsuperIndividual, effectiveAmount, remainingFish; + + // features of the super individuals + // for female + numberOfsuperIndividual = Math.max(1L, + Math.round(numberOfFemaleRecruit / amountPerSuperIndividual)); + effectiveAmount = (long) Math.floor(numberOfFemaleRecruit / numberOfsuperIndividual); + for (long i = 0; i < (numberOfsuperIndividual-1); i++){ + group.addAquaNism(new DiadromousFish(group.getPilot(), riverBasin, initialLength, effectiveAmount, Gender.FEMALE)); } + // the last Super indivial could be larger to include remainging fish + remainingFish = numberOfFemaleRecruit - numberOfsuperIndividual * effectiveAmount; + group.addAquaNism(new DiadromousFish(group.getPilot(), riverBasin, initialLength, effectiveAmount + remainingFish, Gender.FEMALE)); - int numberOfsuperIndividual = Math.max(1, - (int) Math.round(numberOfRecruit / amountPerSuperIndividual)); - long effectiveAmount = numberOfRecruit / numberOfsuperIndividual; - // System.out.println(numberOfRecruit + " / " + amountPerSuperIndividual +" = " +numberOfsuperIndividual); - //System.out.println(numberOfRecruit + " / " + numberOfsuperIndividual +" = " +effectiveAmount); - for (int i=0; i<numberOfsuperIndividual; i++){ - group.addAquaNism(new DiadromousFish(group.getPilot(), riverBasin, initialLength, effectiveAmount)); + // for male + numberOfsuperIndividual = Math.max(1L, + Math.round(numberOfMaleRecruit / amountPerSuperIndividual)); + effectiveAmount = (long) Math.floor(numberOfMaleRecruit / numberOfsuperIndividual); + for (long i = 0; i < (numberOfsuperIndividual-1); i++){ + group.addAquaNism(new DiadromousFish(group.getPilot(), riverBasin, initialLength, effectiveAmount, Gender.MALE)); } + // the last Super indivial could be larger to include remainging fish + remainingFish = numberOfMaleRecruit - numberOfsuperIndividual * effectiveAmount; + group.addAquaNism(new DiadromousFish(group.getPilot(), riverBasin, initialLength, effectiveAmount + remainingFish, Gender.FEMALE)); + + // ---------------------------------------------- + // keep information when reproduction with success + // stock the first year when recruitment is non nul + if (riverBasin.getYearOfFirstNonNulRep() == 0){ + riverBasin.setYearOfFirstNonNulRep(Time.getYear(group.getPilot())); + } + + // keep the last recruitments in the stack riverBasin.getLastRecruitmentExpectations().push(Math.round(meanNumberOfRecruit)); - riverBasin.getLastRecruitments().push(numberOfsuperIndividual * effectiveAmount); // on remplit la pile qui permet de stocker un nombre fixé de derniers recrutement + riverBasin.getLastRecruitments().push(numberOfsuperIndividual * effectiveAmount); riverBasin.getLastRecsOverProdCaps().push(((double) riverBasin.getLastRecruitments().getLastItem())/riverBasin.getLastProdCapacities().getLastItem()); + // keep the no null recruitment if (numberOfAutochtones > 0){ - riverBasin.getNumberOfNonNulRecruitmentForFinalProbOfPres().push(1.0); - riverBasin.getNumberOfNonNulRecruitmentDuringLastYears().push(1.0); + riverBasin.getNumberOfNonNulRecruitmentForFinalProbOfPres().push(1.); + riverBasin.getNumberOfNonNulRecruitmentDuringLastYears().push(1.); }else{ - riverBasin.getNumberOfNonNulRecruitmentForFinalProbOfPres().push(0.0); - riverBasin.getNumberOfNonNulRecruitmentDuringLastYears().push(0.0); + riverBasin.getNumberOfNonNulRecruitmentForFinalProbOfPres().push(0.); + riverBasin.getNumberOfNonNulRecruitmentDuringLastYears().push(0.); } } @@ -292,74 +467,161 @@ public class ReproduceAndSurviveAfterReproductionWithDiagnose extends AquaNismsG riverBasin.getLastRecruitments().push((long) 0); riverBasin.getLastRecsOverProdCaps().push(0.); riverBasin.getNumberOfNonNulRecruitmentDuringLastYears().push(0.); - riverBasin.getNumberOfNonNulRecruitmentForFinalProbOfPres().push(0.0); + riverBasin.getNumberOfNonNulRecruitmentForFinalProbOfPres().push(0.); } } else { // stock information when no spawners reproduce + //System.out.println(riverBasin.getName()+ "\tF:"+numberOfFemaleSpawners+ " M:"+numberOfMaleSpawners); riverBasin.setYearOfLastNulRep(Time.getYear(group.getPilot())); riverBasin.getLastRecruitmentExpectations().push((long) 0); riverBasin.getLastRecruitments().push((long) 0); riverBasin.getLastRecsOverProdCaps().push(0.); riverBasin.getLastPercentagesOfAutochtones().push(0.); riverBasin.getNumberOfNonNulRecruitmentDuringLastYears().push(0.); - riverBasin.getNumberOfNonNulRecruitmentForFinalProbOfPres().push(0.0); + riverBasin.getNumberOfNonNulRecruitmentForFinalProbOfPres().push(0.); } - - // Remove deadfish + // -------------------------------------------------------------------------------------------------- + // Remove deadfish and compute the related nutrient fluxes + // -------------------------------------------------------------------------------------------------- for (DiadromousFish fish : deadFish){ group.removeAquaNism(fish); } deadFish.clear(); + + // ------------------------------------------------------- + // display information + // ----------------------------------------------------- + if (displayFluxesOnConsole) + + System.out.println(group.getPilot().getCurrentTime() + "; " + Time.getYear(group.getPilot()) + ";" + Time.getSeason(group.getPilot()) + ";IMPORT;" + + riverBasin.getName() + ";" + fluxBefore + ";" + riverBasin.getSpawnerNumberPerGroup(group)+ "; " + totalInputFluxes); + BufferedWriter bW = group.getbWForFluxes(); + if ( bW != null) { + try { + for (fluxOrigin origin : totalInputFluxes.keySet()) { + bW.write(group.getPilot().getCurrentTime() + "; " + Time.getYear(group.getPilot()) + ";" + Time.getSeason(group.getPilot()) + +";"+ riverBasin.getName() + ";" + fluxBefore + ";" + "IMPORT"+ ";" + origin); + bW.write(";" + totalInputFluxes.get(origin).get("biomass")); + + for (String nutrient : group.getNutrientRoutine().getNutrientsOfInterest()) { + bW.write(";" + totalInputFluxes.get(origin).get(nutrient)); + } + bW.write("\n"); + } + } catch (IOException e) { + + e.printStackTrace(); + } + } } else { riverBasin.setYearOfLastNulRep(Time.getYear(group.getPilot())); } - // System.out.println("("+numberOfGenitors+")"); + + //System.out.println("("+numberOfGenitors+")"); //System.out.println(" BEFORE " +riverBasin.getSpawnerOrigins().keySet()); riverBasin.getSpawnerOrigins().push(spawnerOriginsDuringReproduction); //System.out.println(" AFTER " +riverBasin.getSpawnerOrigins().keySet()); } - // on met à jour les observeurs + + + // -------------------------------------------------------------------------------------------------- + // update the observers + // -------------------------------------------------------------------------------------------------- for (RiverBasin riverBasin : group.getEnvironment().getRiverBasins()){ riverBasin.getCobservable().fireChanges(riverBasin, pilot.getCurrentTime()); } } } + /** + * Berverton and Holt stock-recruitment relationship with an Allee effect simulated with a logistic function + */ class StockRecruitmentRelationship implements UnivariateFunction{ + /** + * alpha of the stock-recruitment relationship + * R = beta Seff / (beta + Seff) with Seff the stock that effectivly participates to the reproduction + * + * @unit # of individuals + */ double alpha; + + /** + * * beta of the stock-recruitment relationship + * R = alpha * Seff / (beta + Seff) with Seff the stock that effectivly participates to the reproduction + * @unit + */ double beta; + + /** + * the value of the stock for which 50% partipate to the reproduction + * @unit # of individuals + */ double S50; + + /** + * the value of the stock for which 95% partipate to the reproduction + * @unit # of individuals + */ double S95; - double sigmaZ; // mortality + /** + * to simplify the calculation + * @unit + */ + transient double log19; + + /** + * the value of the stock for which 50% partipate to the reproduction + * @unit # of individuals + */ + double sigmaZ; // public void init(double alpha, double beta, double S50, double S95) { this.alpha = alpha; this.beta = beta; this.S50 = S50; this.S95 = S95; + + log19 = Math.log(19) ; } + + public double getEffectiveStock (double stock) { + return stock / (1 + Math.exp(- log19 * (stock - S50) / (S95 - S50))); + } + + public double getRecruitment(double stock){ //BH Stock-Recruitment relationship with logistic depensation double meanNumberOfRecruit = 0.; + double effectiveStock = getEffectiveStock(stock); if (stock >0) - meanNumberOfRecruit= Math.round((alpha * stock * (1 / (1 + Math.exp(- Math.log(19)*((stock - S50) / (S95 - S50)))))) / - (beta + stock * (1 / (1 + Math.exp(- Math.log(19)*((stock - S50) / (S95 - S50))))))); + meanNumberOfRecruit = Math.round(alpha * effectiveStock) /(beta + effectiveStock ); return meanNumberOfRecruit; } + + /** + * the stock that corresponds to the intersection between SR relationship and tahe tangent that pass through the origin + * @return the stock at + */ public double getStockAtZcrash(){ if (beta !=0) - return(S50 + (S95 - S50) * Math.log(beta * Math.log(19) / (S95-S50)) / Math.log(19)); + return(S50 + (S95 - S50) * Math.log(beta * log19 / (S95-S50)) / log19); else return Double.NaN; } + + /** + * the crash mortality + * (corresponds the slope of the tangent that pass through the origin) + * @return the crash mortality ( year-1) + */ public double getSigmaZcrash(){ double stockAtZcrash= getStockAtZcrash(); if (!Double.isNaN(stockAtZcrash)) @@ -369,13 +631,23 @@ public class ReproduceAndSurviveAfterReproductionWithDiagnose extends AquaNismsG } + /** + * the objective function uses to calculate the depensation trap (Allee trap) + */ @Override - public double value(double S) { - double res=getRecruitment(S)-S*Math.exp(sigmaZ); + public double value(double stock) { + double res=getRecruitment(stock) - stock * Math.exp(sigmaZ); return res*res; } - private double getPoint(double sigmaZ){ + + /** + * calculate the stock correspondinf to the depensation trap + * corresponds to intersection between mortality rate and the stock-recruitment relationship + * @param sigmaZ the total mortality coefficient + * @return + */ + private double getStockTrap(double sigmaZ){ if (!Double.isNaN(sigmaZ)){ this.sigmaZ=sigmaZ; BrentOptimizer optimizer = new BrentOptimizer(1e-6, 1e-12); @@ -384,25 +656,23 @@ public class ReproduceAndSurviveAfterReproductionWithDiagnose extends AquaNismsG new MaxEval(100), GoalType.MINIMIZE, new SearchInterval(0, getStockAtZcrash())); - this.sigmaZ = Double.NaN; + this.sigmaZ = Double.NaN; //WHY return solution.getPoint(); } else return Double.NaN; } - - public double getStockTrap(double sigmaZ) { - return getPoint(sigmaZ); - } - } + /** + * mortatiity function for stock with dirrenet ages + */ class MortalityFunction implements UnivariateFunction { - // first age - // second effective of native spwaner - // third effective of corresponding recruitment - List<Trio<Integer, Long, Long>> data; + // first: age + // second: effective of native spwaner + // third: effective of corresponding recruitment + List<Trio<Integer, Long, Long>> data; //WHY age as integer public void init(List<Trio<Integer, Long, Long>> data){ this.data = data; @@ -418,50 +688,45 @@ public class ReproduceAndSurviveAfterReproductionWithDiagnose extends AquaNismsG return res/effTotal; } + @Override public double value(double Z) { double res=0.; for(Trio<Integer, Long, Long> trio : data){ res += (((double) trio.getSecond())/((double) trio.getThird())) * Math.exp(Z * (double) trio.getFirst()); } - return Math.pow(res-1., 2.); + return (res-1) * (res-1); //WHY -1 } - private double getPoint(){ - if (data.isEmpty()){ - return Double.NaN; - } - else { + + /** + * calculate by optimsation of the total mortality coefficient over the livespan + * @return + */ + private double getSigmaZ2(){ + double Z = Double.NaN; + + if (!data.isEmpty()){ BrentOptimizer optimizer = new BrentOptimizer(1e-6, 1e-12); UnivariatePointValuePair solution = optimizer.optimize(new UnivariateObjectiveFunction(this), new MaxEval(100), GoalType.MINIMIZE, new SearchInterval(0, 10)); - return solution.getPoint(); + Z= solution.getPoint(); } + return Z * getMeanAge(); } - // return sigmaZ = Z*meanAge - public double getSigmaZ2(){ - /*double Z = getPoint(); - double alpha; // % of maturation for a given age - double sum=0; - double sum1=0; - for(Trio<Integer, Long, Long> trio : data){ - alpha = ((double)trio.getSecond()) / (((double)trio.getThird())*Math.exp(-((double) trio.getFirst())*Z)); - sum += alpha * Math.exp(-((double) trio.getFirst())*Z); - sum1 += ((double)trio.getSecond()) / ((double)trio.getThird()); - } - - System.out.println(getPoint()*getMeanAge()+" <> "+ Math.log(sum)+ " <> " + Math.log(sum1));*/ - return getPoint()*getMeanAge(); - } - + + /** + * simple approximation of total mortality coefficient over the lifespan + * @return + */ public double getSigmaZ(){ double sum=0; for(Trio<Integer, Long, Long> trio : data){ - sum += ((double)trio.getSecond()) / ((double)trio.getThird()); + sum += ((double)trio.getSecond()) / ((double)trio.getThird()); } return (- Math.log(sum)); } diff --git a/src/main/java/species/Survive.java b/src/main/java/species/Survive.java index 1b3149314c1893e22bd95c8a28228ed7d08c386f..2b6fc5a4371e39f57704cd373efcb7aaffa49dbd 100644 --- a/src/main/java/species/Survive.java +++ b/src/main/java/species/Survive.java @@ -33,44 +33,44 @@ public class Survive extends AquaNismsGroupProcess<DiadromousFish, DiadromousFis @Override public void doProcess(DiadromousFishGroup group) { - // TODO Auto-generated method stub - double survivalProbability; + + double survivalProbability=1.; List<DiadromousFish> deadFish = new ArrayList<DiadromousFish>(); long survivalAmount; for(Basin basin : group.getEnvironment().getBasins()){ - if (basin.getFishs(group)!=null) for(DiadromousFish fish : basin.getFishs(group)){ - survivalProbability = 1.; - //Survive - if(fish.getPosition().getType() == TypeBassin.RIVER && fish.isMature()){ - double tempEffectSurv = Miscellaneous.temperatureEffect(fish.getPosition().getCurrentTemperature(group.getPilot()), tempMinMortGenInRiv, tempOptMortGenInRiv, tempMaxMortGenInRiv); - if (tempEffectSurv == 0.){ - survivalProbability = 0.; - //System.out.println("le poisson situé dans le bassin " + fish.getPosition().getName() + " en " + Time.getSeason() +" a un coeff de mortalité de " + fish.getMortalityRateInRiver() + " mais à cause de la température une prob de survie de " + survivalProbability); - }else{ - survivalProbability = survivalProbOptGenInRiv * tempEffectSurv; - //System.out.println("le poisson situé dans le bassin " + fish.getPosition().getName() + " en " + Time.getSeason() + " a un coeff de mortalité de " + fish.getMortalityRateInRiver() + " et donc une prob de survie de " + survivalProbability); - } - }else if (fish.getPosition().getType() == TypeBassin.SEA){ - survivalProbability = Math.exp(-mortalityRateInSea * Time.getSeasonDuration()); - //System.out.println("le poisson situé dans le bassin " + fish.getPosition().getName() + " en " + Time.getSeason() + " a un coeff de mortalité de " + fish.getMortalityRateInSea() + " et donc une prob de survie de " + survivalProbability); - }else if (fish.getPosition().getType() == TypeBassin.OFFSHORE){ - survivalProbability = Math.exp(-mortalityRateInOffshore * Time.getSeasonDuration()); - //System.out.println("le poisson situé dans le bassin " + fish.getPosition().getName() + " en " + Time.getSeason() + " a un coeff de mortalité de " + fish.getMortalityRateInOffshore() + " et donc une prob de survie de " + survivalProbability); - }else{ + if (basin.getFishs(group)!=null) for(DiadromousFish fish : basin.getFishs(group)){ survivalProbability = 1.; - } + //Survive + if(fish.getPosition().getType() == TypeBassin.RIVER && fish.isMature()){ + double tempEffectSurv = Miscellaneous.temperatureEffect(fish.getPosition().getCurrentTemperature(group.getPilot()), tempMinMortGenInRiv, tempOptMortGenInRiv, tempMaxMortGenInRiv); + if (tempEffectSurv == 0.){ + survivalProbability = 0.; + //System.out.println("le poisson situ� dans le bassin " + fish.getPosition().getName() + " en " + Time.getSeason() +" a un coeff de mortalit� de " + fish.getMortalityRateInRiver() + " mais � cause de la temp�rature une prob de survie de " + survivalProbability); + }else{ + survivalProbability = survivalProbOptGenInRiv * tempEffectSurv; + //System.out.println("le poisson situ� dans le bassin " + fish.getPosition().getName() + " en " + Time.getSeason() + " a un coeff de mortalit� de " + fish.getMortalityRateInRiver() + " et donc une prob de survie de " + survivalProbability); + } + }else if (fish.getPosition().getType() == TypeBassin.SEA){ + survivalProbability = Math.exp(-mortalityRateInSea * Time.getSeasonDuration()); + //System.out.println("le poisson situ� dans le bassin " + fish.getPosition().getName() + " en " + Time.getSeason() + " a un coeff de mortalit� de " + fish.getMortalityRateInSea() + " et donc une prob de survie de " + survivalProbability); + }else if (fish.getPosition().getType() == TypeBassin.OFFSHORE){ + survivalProbability = Math.exp(-mortalityRateInOffshore * Time.getSeasonDuration()); + //System.out.println("le poisson situ� dans le bassin " + fish.getPosition().getName() + " en " + Time.getSeason() + " a un coeff de mortalit� de " + fish.getMortalityRateInOffshore() + " et donc une prob de survie de " + survivalProbability); + }else{ + survivalProbability = 1.; + } - if (survivalProbability<1.){ - survivalAmount = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), survivalProbability); + if (survivalProbability<1.){ + survivalAmount = Miscellaneous.binomialForSuperIndividual(group.getPilot(), fish.getAmount(), survivalProbability); - if (survivalAmount > 0) - fish.setAmount(survivalAmount); - else - deadFish.add(fish); + if (survivalAmount > 0) + fish.setAmount(survivalAmount); + else + deadFish.add(fish); + } } - } - } + } for (DiadromousFish fish : deadFish){ group.removeAquaNism(fish); diff --git a/src/main/java/species/TypeTrajectoryCV.java b/src/main/java/species/TypeTrajectoryCV.java index 054f01da03069020f5fa5578fded84a2ac97ae57..5dfcbef96d8765d1436a52553d7e412b068166b5 100644 --- a/src/main/java/species/TypeTrajectoryCV.java +++ b/src/main/java/species/TypeTrajectoryCV.java @@ -116,7 +116,7 @@ public class TypeTrajectoryCV extends AquaNismsGroupProcess<DiadromousFish, Diad bW.write("likelihood;" + ((Double) group.computeLikelihood()).toString() + "\n"); - bW.write("spawnersMatureAgeSumStat;" + group.computeSpawnerForFirstTimeSummaryStatistic() + "\n"); + bW.write("spawnersMatureAgeSumStat;" + group.computeFemaleSpawnerForFirstTimeSummaryStatistic() + "\n"); bW.write("higherPopulatedLatitude;" + group.getHigherPopulatedLatitude() + "\n");