## ams_version=1.0 Model Main_FileMerge { Comment: { "Keywords: Linear Program, Network Program, Simplex Method, Column Generation, Mathematical Derivation, Customized Algorithm." } Section Model_Formulation { DeclarationSection File_Merge { Set AllRecords { Index: a; Definition: IncomeRecords+PopulationRecords; Comment: "Data set of both income and population records"; } Set IncomeRecords { SubsetOf: AllRecords; Index: i; Parameter: istar; } Set PopulationRecords { SubsetOf: AllRecords; Index: j; Parameter: jstar; } Set Permitted { SubsetOf: (IncomeRecords,PopulationRecords); } Parameter GrossIncome { IndexDomain: (a); Comment: "Gross income of each data set"; } Parameter Members { IndexDomain: (a); Comment: "Number of family members of each data set"; } Parameter IncomeSources { IndexDomain: (a); Comment: "Income source of each data set"; } Parameter Interest { IndexDomain: (a); Comment: "Interest of each data set"; } Parameter NumberOfFamilies { IndexDomain: (a); Comment: "Total number of families in each data set"; } Parameter TempNumberOfFamilies { IndexDomain: (a); Comment: "Temporary variables, number of families, is used during the excution"; } Parameter AgeHead { IndexDomain: (a); Comment: "Age head in each data set"; } Parameter YearsEducation { IndexDomain: (a); Comment: "Years of education of house hold in each data set"; } Parameter Gender { IndexDomain: (a); Comment: "Gendar of house hold in each data set"; } Parameter Distance { IndexDomain: (i,j) | (i,j) in Permitted; Comment: "The variance of gross income and number of family members between two files"; } Parameter EstimatedVarianceGrossIncome { Definition: sqr[SampleDeviation(a,GrossIncome(a))]; Comment: "Variance of gross income between two data files"; } Parameter EstimatedVarianceMembers { Definition: sqr[SampleDeviation(a,Members(a))]; Comment: "Variance of number of family members between two data files"; } Parameter TemporaryNumberOfFamilies; Macro DistanceFormula { Arguments: (i,j); Definition: { sqrt[ sqr(GrossIncome(i)-GrossIncome(j)) /EstimatedVarianceGrossIncome+ sqr(Members(i)-Members(j)) /EstimatedVarianceMembers] } Comment: "The total variance of gross income and number of family members between two data files"; } Variable Merged { IndexDomain: (i,j) | (i,j) in Permitted; Range: nonnegative; Comment: "The merged data table with the number of merged families"; } Variable TotalDistance { Definition: Sum((i,j),Distance(i,j)*Merged(i,j)); Comment: "Total distance = ditance * number of merged families"; } Constraint IncomeRecordSupply { IndexDomain: (i); Property: ShadowPrice; Definition: Sum(j,Merged(i,j))=NumberOfFamilies(i); Comment: "Constraints of supply"; } Constraint PopulationRecordDemand { IndexDomain: (j); Property: ShadowPrice; Definition: Sum(i,Merged(i,j))=NumberOfFamilies(j); Comment: "Constraints of demand"; } MathematicalProgram FileMerge { Objective: TotalDistance; Direction: minimize; Constraints: AllConstraints; Variables: AllVariables; Type: LP; } } Section SupportRoutines { Procedure DetermineInitialSolution { Body: { TempNumberOfFamilies(a):=NumberOfFamilies(a); Repeat istar := First(i|TempNumberOfFamilies(i)>0); jstar := First(j|TempNumberOfFamilies(j)>0); Break When Not(istar And jstar); TemporaryNumberOfFamilies := min(TempNumberOfFamilies(istar),TempNumberOfFamilies(jstar)); Merged(istar,jstar) := TemporaryNumberOfFamilies; TempNumberOfFamilies(istar) -= TemporaryNumberOfFamilies; TempNumberOfFamilies(jstar) -= TemporaryNumberOfFamilies; Permitted += {(istar,jstar)}; Distance(istar,jstar) := DistanceFormula(istar,jstar); EndRepeat; } Comment: "Empty Permitted , Distance , Merged"; } Procedure DetermineBestCandidates { Body: { FOR i DO FOR j DO TemporaryDistance(j) := DistanceFormula(i,j); ENDFOR; TemporaryMax := max [ j , TemporaryDistance(j) ]; REPEAT BREAK WHEN (LoopCount > NumberOfBestCandidates); jstar := argmin(j , TemporaryDistance(j)); Permitted += { (i,jstar) }; Distance(i,jstar) := DistanceFormula(i,jstar); TemporaryDistance(jstar) := TemporaryMax; ENDREPEAT; ENDFOR; FOR j DO FOR i DO TemporaryDistance(i) := DistanceFormula(i,j); ENDFOR; TemporaryMax := max [ i , TemporaryDistance(i) ]; REPEAT BREAK WHEN (LoopCount > NumberOfBestCandidates); istar := argmin(i , TemporaryDistance(i)); Permitted += { (istar,j) }; Distance(istar,j) := DistanceFormula(istar,j); TemporaryDistance(istar) := TemporaryMax; ENDREPEAT; ENDFOR; } Comment: "Find best candidates for given supply and demand"; Parameter TemporaryDistance { IndexDomain: (a); } Parameter TemporaryMax; Parameter NumberOfBestCandidates { InitialData: 4; } } Procedure DetermineAllCandidateVariables { Body: { Repeat Solve FileMerge; istar:=First(IncomeRecords); jstar:=First(PopulationRecords); SearchCount:=0; CandidateCount:=0; Repeat TempDistance:=DistanceFormula(istar,jstar); If ((TempDistance-IncomeRecordSupply.ShadowPrice(istar) -PopulationRecordDemand.ShadowPrice(jstar))<=(0-Epsilon)) Then Permitted+={(istar,jstar)}; Distance(istar,jstar):=TempDistance; CandidateCount+=1; Endif; jstar+=1; If (jstar='') Then istar+=1; jstar:=First(PopulationRecords); ! SearchCount+=1; Endif; SearchCount+=1; Break when (SearchCount=(card(IncomeRecords)*card(PopulationRecords))); Endrepeat; Break when (CandidateCount=0); Endrepeat; } Comment: "Find the optimal solution through systematically solving a sequence of smaller submodels"; Parameter SearchCount; Parameter CandidateCount; Parameter Epsilon { InitialData: 1E-3; } Parameter TempDistance; } } Section AlternativeMethods { Procedure RunWithAllVariables { Body: { MainInitialization; Permitted:={(i,j)}; Empty Merged; Distance(i,j):=DistanceFormula(i,j); Solve FileMerge; } } Procedure RunWithInitialSolutionOnly { Body: { MainInitialization; DetermineInitialSolution; Solve FileMerge; } } Procedure RunWithColumnGeneration { Body: { MainInitialization; Empty Permitted; Empty Merged; DetermineInitialSolution; DetermineBestCandidates; DetermineAllCandidateVariables; } } } } Section Interface_Section { DeclarationSection Output_Data { Set NewRecord; Parameter TotalRecords { Definition: Card(Permitted); Comment: "Number of new records"; } Parameter Merged_GrossIncome { IndexDomain: (i,j); Definition: { If Merged(i,j)then (GrossIncome(i)+GrossIncome(j))/2 endif } Comment: "Merged gross income is equal to sum of the gross incomes of the two data sets divided by two"; } Parameter Merged_Members { IndexDomain: (i,j); Definition: { If Merged(i,j)then Max(Members(i),Members(j)) endif } Comment: "Merged members is the maximum number of family members"; } Parameter Merged_IncomeSources { IndexDomain: (i,j); Definition: { If Merged(i,j) then IncomeSources(i) endif } Comment: "Merged income source is called from the income data file"; } Parameter Merged_Interest { IndexDomain: (i,j); Definition: { If Merged(i,j) then Interest(i) endif } Comment: "Merged interest is called from the income data file"; } Parameter Merged_NumberOfFamilies { IndexDomain: (i,j); Definition: { If Merged(i,j)then min(NumberOfFamilies(i),NumberOfFamilies(j)) endif } Comment: "Merged number of families is equal to the minimum number of families from the two data sets"; } Parameter Merged_TempNumberOfFamilies { IndexDomain: (i,j); Comment: "Temporary number of families = 0"; } Parameter Merged_AgeHead { IndexDomain: (i,j); Definition: { If Merged(i,j)then AgeHead(j) endif } Comment: "Merged age head is called from the population data file"; } Parameter Merged_YearsEducation { IndexDomain: (i,j); Definition: { If Merged(i,j)then YearsEducation(j) endif } Comment: "Merged year education is called from the population data file"; } Parameter Merged_Gender { IndexDomain: (i,j); Definition: { If Merged(i,j)then Gender(j) endif } Comment: "Merged gender is called from the population data file"; } } StringParameter ActivePage { Definition: { If Solved then "Merged Data Table" else "Income Data Table" endif; } } Parameter Solved { Default: 0; InitialData: { 0; } } ElementParameter ACase { Range: AllCases; } } Section SystemRoutines { Procedure MainInitialization { Body: { !-- read data from a project user file (menu Tools - Project User Files) Read From file ":datafilemerge.txt" In merge mode; } } Procedure MainExecution; Procedure MainTermination { Body: { return 1; } } } }