/** * Copyright 2010 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.drools.planner.examples.pas.persistence; import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Collections; import org.drools.planner.core.solution.Solution; import org.drools.planner.examples.common.persistence.AbstractTxtSolutionImporter; import org.drools.planner.examples.pas.domain.AdmissionPart; import org.drools.planner.examples.pas.domain.Bed; import org.drools.planner.examples.pas.domain.Department; import org.drools.planner.examples.pas.domain.DepartmentSpecialism; import org.drools.planner.examples.pas.domain.Equipment; import org.drools.planner.examples.pas.domain.Gender; import org.drools.planner.examples.pas.domain.GenderLimitation; import org.drools.planner.examples.pas.domain.Night; import org.drools.planner.examples.pas.domain.Patient; import org.drools.planner.examples.pas.domain.PatientAdmissionSchedule; import org.drools.planner.examples.pas.domain.PreferredPatientEquipment; import org.drools.planner.examples.pas.domain.RequiredPatientEquipment; import org.drools.planner.examples.pas.domain.Room; import org.drools.planner.examples.pas.domain.RoomEquipment; import org.drools.planner.examples.pas.domain.RoomSpecialism; import org.drools.planner.examples.pas.domain.Specialism; /** * @author Geoffrey De Smet * @author Wim Vancroonenburg */ public class PatientAdmissionScheduleSolutionImporter extends AbstractTxtSolutionImporter { public static void main(String[] args) { new PatientAdmissionScheduleSolutionImporter().convertAll(); } public PatientAdmissionScheduleSolutionImporter() { super(new PatientAdmissionScheduleDaoImpl()); } public TxtInputBuilder createTxtInputBuilder() { return new PatientAdmissionScheduleInputBuilder(); } public class PatientAdmissionScheduleInputBuilder extends TxtInputBuilder { private PatientAdmissionSchedule patientAdmissionSchedule; private int specialismListSize; private int departmentListSize; private int equipmentListSize; private int roomListSize; private int bedListSize; private int nightListSize; private int patientListSize; private Map<Long, Specialism> idToSpecialismMap = null; private Map<Long, Department> idToDepartmentMap = null; private Map<Integer, Equipment> indexToEquipmentMap = null; private Map<Long, Room> idToRoomMap = null; private Map<Integer, Night> indexToNightMap = null; public Solution readSolution() throws IOException { patientAdmissionSchedule = new PatientAdmissionSchedule(); patientAdmissionSchedule.setId(0L); readSizes(); readEmptyLine(); readEmptyLine(); readSpecialismList(); readEmptyLine(); readDepartmentListAndDepartmentSpecialismList(); readEmptyLine(); readEquipmentList(); readEmptyLine(); readRoomListAndRoomSpecialismListAndRoomEquipmentList(); readEmptyLine(); readBedList(); readEmptyLine(); generateNightList(); readPatientListAndAdmissionPartListAndRequiredPatientEquipmentListAndPreferredPatientEquipmentList(); readEmptyLine(); readConstantLine("END."); // TODO not all nights are planned, only the "planning horizon" nights are planned logger.info("PatientAdmissionSchedule with {} specialisms, {} equipments, {} departments, {} rooms, " + "{} beds, {} nights, {} patients and {} admissions.", new Object[]{patientAdmissionSchedule.getSpecialismList().size(), patientAdmissionSchedule.getEquipmentList().size(), patientAdmissionSchedule.getDepartmentList().size(), patientAdmissionSchedule.getRoomList().size(), patientAdmissionSchedule.getBedList().size(), patientAdmissionSchedule.getNightList().size(), patientAdmissionSchedule.getPatientList().size(), patientAdmissionSchedule.getAdmissionPartList().size()}); BigInteger possibleSolutionSize = BigInteger.valueOf(patientAdmissionSchedule.getBedList().size()).pow( patientAdmissionSchedule.getAdmissionPartList().size()); String flooredPossibleSolutionSize = "10^" + (possibleSolutionSize.toString().length() - 1); logger.info("PatientAdmissionSchedule with flooredPossibleSolutionSize ({}) and possibleSolutionSize({}).", flooredPossibleSolutionSize, possibleSolutionSize); return patientAdmissionSchedule; } private void readSizes() throws IOException { readConstantLine("ARTICLE BENCHMARK DATA SET"); roomListSize = readIntegerValue("Rooms:"); equipmentListSize = readIntegerValue("Roomproperties:"); bedListSize = readIntegerValue("Beds:"); departmentListSize = readIntegerValue("Departments:"); specialismListSize = readIntegerValue("Specialisms:"); patientListSize = readIntegerValue("Patients:"); nightListSize = readIntegerValue("Planning horizon:"); } private void readSpecialismList() throws IOException { readConstantLine("SPECIALISMS:"); List<Specialism> specialismList = new ArrayList<Specialism>(specialismListSize); idToSpecialismMap = new HashMap<Long, Specialism>(specialismListSize); for (int i = 0; i < specialismListSize; i++) { String line = bufferedReader.readLine(); String[] lineTokens = splitBySpace(line, 2); Specialism specialism = new Specialism(); specialism.setId(Long.parseLong(lineTokens[0])); specialism.setName(lineTokens[1]); specialismList.add(specialism); idToSpecialismMap.put(specialism.getId(), specialism); } patientAdmissionSchedule.setSpecialismList(specialismList); } private void readDepartmentListAndDepartmentSpecialismList() throws IOException { readConstantLine("DEPARTMENTS:"); List<Department> departmentList = new ArrayList<Department>(departmentListSize); idToDepartmentMap = new HashMap<Long, Department>(departmentListSize); List<DepartmentSpecialism> departmentSpecialismList = new ArrayList<DepartmentSpecialism>( departmentListSize * 5); long departmentSpecialismId = 0L; for (int i = 0; i < departmentListSize; i++) { String line = bufferedReader.readLine(); String[] lineTokens = splitByPipeline(line, 2); String[] departmentTokens = splitBySpace(lineTokens[0], 4); Department department = new Department(); department.setId(Long.parseLong(departmentTokens[0])); department.setName(departmentTokens[1]); int minimumAge = Integer.parseInt(departmentTokens[2]); if (minimumAge != 0) { department.setMinimumAge(Integer.valueOf(minimumAge)); } int maximumAge = Integer.parseInt(departmentTokens[3]); if (maximumAge != 0) { department.setMaximumAge(Integer.valueOf(maximumAge)); } departmentList.add(department); idToDepartmentMap.put(department.getId(), department); String[] departmentSpecialismTokens = splitBySpace(lineTokens[1]); if (departmentSpecialismTokens.length % 2 != 0) { throw new IllegalArgumentException("Read line (" + line + ") is expected to contain even number of tokens (" + departmentSpecialismTokens.length + ") after 1st pipeline (|) seperated by a space ( )."); } for (int j = 0; j < departmentSpecialismTokens.length; j += 2) { long specialismId = Long.parseLong(departmentSpecialismTokens[j + 1]); if (specialismId != 0) { DepartmentSpecialism departmentSpecialism = new DepartmentSpecialism(); departmentSpecialism.setId(departmentSpecialismId); departmentSpecialism.setDepartment(department); departmentSpecialism.setPriority(Integer.parseInt(departmentSpecialismTokens[j])); Specialism specialism = idToSpecialismMap.get(specialismId); if (specialism == null) { throw new IllegalArgumentException("Read line (" + line + ") has a non existing specialismId (" + specialismId + ")."); } departmentSpecialism.setSpecialism(specialism); departmentSpecialismList.add(departmentSpecialism); departmentSpecialismId++; } } } Collections.sort(departmentList); patientAdmissionSchedule.setDepartmentList(departmentList); patientAdmissionSchedule.setDepartmentSpecialismList(departmentSpecialismList); } private void readEquipmentList() throws IOException { readConstantLine("ROOMPROPERTIES:"); List<Equipment> equipmentList = new ArrayList<Equipment>(equipmentListSize); indexToEquipmentMap = new HashMap<Integer, Equipment>(equipmentListSize); for (int i = 0; i < equipmentListSize; i++) { String line = bufferedReader.readLine(); String[] lineTokens = splitBySpace(line, 2); Equipment equipment = new Equipment(); equipment.setId(Long.parseLong(lineTokens[0])); equipment.setName(lineTokens[1]); equipmentList.add(equipment); indexToEquipmentMap.put(i, equipment); } patientAdmissionSchedule.setEquipmentList(equipmentList); } private void readRoomListAndRoomSpecialismListAndRoomEquipmentList() throws IOException { readConstantLine("ROOMS:"); List<Room> roomList = new ArrayList<Room>(roomListSize); idToRoomMap = new HashMap<Long, Room>(roomListSize); List<RoomSpecialism> roomSpecialismList = new ArrayList<RoomSpecialism>(roomListSize * 5); List<RoomEquipment> roomEquipmentList = new ArrayList<RoomEquipment>(roomListSize * 2); long roomSpecialismId = 0L; long roomEquipmentId = 0L; for (int i = 0; i < roomListSize; i++) { String line = bufferedReader.readLine(); String[] lineTokens = splitByPipeline(line, 6); String[] roomTokens = splitBySpace(lineTokens[0], 2); Room room = new Room(); room.setId(Long.parseLong(roomTokens[0])); room.setName(roomTokens[1]); room.setCapacity(Integer.parseInt(lineTokens[1])); room.setDepartment(idToDepartmentMap.get( Long.parseLong(lineTokens[2]))); room.setGenderLimitation(GenderLimitation.valueOfCode(lineTokens[3])); roomList.add(room); idToRoomMap.put(room.getId(), room); String[] roomSpecialismTokens = splitBySpace(lineTokens[4]); if (roomSpecialismTokens.length % 2 != 0) { throw new IllegalArgumentException("Read line (" + line + ") is expected to contain even number of tokens (" + roomSpecialismTokens.length + ") after 4th pipeline (|) seperated by a space ( )."); } List<RoomSpecialism> roomSpecialismListOfRoom = new ArrayList<RoomSpecialism>(roomSpecialismTokens.length / 2); for (int j = 0; j < roomSpecialismTokens.length; j += 2) { int priority = Integer.parseInt(roomSpecialismTokens[j]); long specialismId = Long.parseLong(roomSpecialismTokens[j + 1]); if (specialismId != 0) { RoomSpecialism roomSpecialism = new RoomSpecialism(); roomSpecialism.setId(roomSpecialismId); roomSpecialism.setRoom(room); Specialism specialism = idToSpecialismMap.get(specialismId); if (specialism == null) { throw new IllegalArgumentException("Read line (" + line + ") has a non existing specialismId (" + specialismId + ")."); } roomSpecialism.setSpecialism(specialism); roomSpecialism.setPriority(priority); roomSpecialismListOfRoom.add(roomSpecialism); roomSpecialismList.add(roomSpecialism); roomSpecialismId++; } } room.setRoomSpecialismList(roomSpecialismListOfRoom); List<RoomEquipment> roomEquipmentListOfRoom = new ArrayList<RoomEquipment>(equipmentListSize); String[] roomEquipmentTokens = splitBySpace(lineTokens[5]); if (roomEquipmentTokens.length != equipmentListSize) { throw new IllegalArgumentException("Read line (" + line + ") is expected to contain equal number of tokens (" + roomEquipmentTokens.length + ") as equipmentListSize (" + equipmentListSize + ") after 5th pipeline (|)."); } for (int j = 0; j < roomEquipmentTokens.length; j++) { int hasEquipment = Integer.parseInt(roomEquipmentTokens[j]); if (hasEquipment == 1) { RoomEquipment roomEquipment = new RoomEquipment(); roomEquipment.setId(roomEquipmentId); roomEquipment.setRoom(room); roomEquipment.setEquipment(indexToEquipmentMap.get(j)); roomEquipmentListOfRoom.add(roomEquipment); roomEquipmentList.add(roomEquipment); roomEquipmentId++; } else if (hasEquipment != 0) { throw new IllegalArgumentException("Read line (" + line + ") is expected to have 0 or 1 hasEquipment (" + hasEquipment + ")."); } } room.setRoomEquipmentList(roomEquipmentListOfRoom); } Collections.sort(roomList); patientAdmissionSchedule.setRoomList(roomList); patientAdmissionSchedule.setRoomSpecialismList(roomSpecialismList); patientAdmissionSchedule.setRoomEquipmentList(roomEquipmentList); } private void readBedList() throws IOException { readConstantLine("BEDS:"); List<Bed> bedList = new ArrayList<Bed>(bedListSize); Map<Room, Integer> roomToLastIndexInRoomMap = new HashMap<Room, Integer>(roomListSize); for (int i = 0; i < bedListSize; i++) { String line = bufferedReader.readLine(); String[] lineTokens = splitBySpace(line, 2); Bed bed = new Bed(); bed.setId(Long.parseLong(lineTokens[0])); Room room = idToRoomMap.get(Long.parseLong(lineTokens[1])); bed.setRoom(room); Integer indexInRoom = roomToLastIndexInRoomMap.get(room); if (indexInRoom == null) { indexInRoom = 0; } else { indexInRoom++; } bed.setIndexInRoom(indexInRoom); roomToLastIndexInRoomMap.put(room, indexInRoom); bedList.add(bed); } Collections.sort(bedList); patientAdmissionSchedule.setBedList(bedList); } private void generateNightList() { List<Night> nightList = new ArrayList<Night>(nightListSize); indexToNightMap = new HashMap<Integer, Night>(nightListSize); long nightId = 0L; for (int i = 0; i < nightListSize; i++) { Night night = new Night(); night.setId(nightId); night.setIndex(i); nightList.add(night); indexToNightMap.put(i, night); nightId++; } patientAdmissionSchedule.setNightList(nightList); } private void readPatientListAndAdmissionPartListAndRequiredPatientEquipmentListAndPreferredPatientEquipmentList() throws IOException { readConstantLine("PATIENTS:"); List<Patient> patientList = new ArrayList<Patient>(patientListSize); List<AdmissionPart> admissionPartList = new ArrayList<AdmissionPart>(patientListSize); List<RequiredPatientEquipment> requiredPatientEquipmentList = new ArrayList<RequiredPatientEquipment>(patientListSize * equipmentListSize); List<PreferredPatientEquipment> preferredPatientEquipmentList = new ArrayList<PreferredPatientEquipment>(patientListSize * equipmentListSize); long admissionPartId = 0L; long requiredPatientEquipmentId = 0L; long preferredPatientEquipmentId = 0L; for (int i = 0; i < patientListSize; i++) { String line = bufferedReader.readLine(); String[] lineTokens = splitByPipeline(line, 6); String[] nightTokens = splitBySpace(lineTokens[1], 2); int firstNightIndex = Integer.parseInt(nightTokens[0]); int lastNightIndex = Integer.parseInt(nightTokens[1]); int patientNightListSize = lastNightIndex - firstNightIndex; // A patient with no nights in the planning horizon or no nights at all is ignored if (firstNightIndex >= nightListSize || patientNightListSize == 0) { continue; } String[] patientTokens = splitBySpace(lineTokens[0], 4); Patient patient = new Patient(); patient.setId(Long.parseLong(patientTokens[0])); patient.setName(patientTokens[1]); patient.setAge(Integer.parseInt(patientTokens[2])); patient.setGender(Gender.valueOfCode(patientTokens[3])); int preferredMaximumRoomCapacity = Integer.parseInt(lineTokens[3]); patient.setPreferredMaximumRoomCapacity(preferredMaximumRoomCapacity == 0 ? null : preferredMaximumRoomCapacity); patientList.add(patient); String[] admissionPartTokens = splitBySpace(lineTokens[2]); if (admissionPartTokens.length % 2 != 1) { } int patientAdmissionPartListSize = Integer.parseInt(admissionPartTokens[0]); if (admissionPartTokens.length != ((patientAdmissionPartListSize * 2) + 1)) { throw new IllegalArgumentException("Read line (" + line + ") is expected to contain " + ((patientAdmissionPartListSize * 2) + 1) + " number of tokens after 2th pipeline (|)."); } int nextFirstNightIndex = firstNightIndex; for (int j = 1; j < admissionPartTokens.length; j += 2) { long specialismId = Long.parseLong(admissionPartTokens[j]); int admissionPartNightListSize = Integer.parseInt(admissionPartTokens[j + 1]); if (nextFirstNightIndex >= nightListSize || admissionPartNightListSize == 0) { nextFirstNightIndex += admissionPartNightListSize; continue; } AdmissionPart admissionPart = new AdmissionPart(); admissionPart.setId(admissionPartId); admissionPart.setPatient(patient); Specialism specialism = (specialismId == 0) ? null : idToSpecialismMap.get(specialismId); if (specialism == null) { throw new IllegalArgumentException("Read line (" + line + ") has a non existing specialismId (" + specialismId + ")."); } admissionPart.setSpecialism(specialism); int admissionPartFirstNightIndex = nextFirstNightIndex; Night admissionPartFirstNight = indexToNightMap.get(admissionPartFirstNightIndex); if (admissionPartFirstNight == null) { throw new IllegalStateException( "The admissionPartFirstNight was not found for admissionPartFirstNightIndex(" + admissionPartFirstNightIndex + ")."); } admissionPart.setFirstNight(admissionPartFirstNight); int admissionPartLastNightIndex = nextFirstNightIndex + admissionPartNightListSize - 1; // TODO Instead of ensureEnoughNights(lastNightIndex); // the official score function ignores any broken constraints after the planning horizon if (admissionPartLastNightIndex >= nightListSize) { admissionPartLastNightIndex = nightListSize - 1; } Night admissionPartLastNight = indexToNightMap.get(admissionPartLastNightIndex); if (admissionPartLastNight == null) { throw new IllegalStateException( "The admissionPartLastNight was not found for admissionPartLastNightIndex(" + admissionPartLastNightIndex + ")."); } admissionPart.setLastNight(admissionPartLastNight); admissionPartList.add(admissionPart); admissionPartId++; nextFirstNightIndex += admissionPartNightListSize; } int admissionPartNightListSizeSum = nextFirstNightIndex - firstNightIndex; if (patientNightListSize != admissionPartNightListSizeSum) { throw new IllegalArgumentException("Read line (" + line + ") has patientNightListSize (" + patientNightListSize + ") different from admissionPartNightListSizeSum(" + admissionPartNightListSizeSum + ")"); } String[] requiredPatientEquipmentTokens = splitBySpace(lineTokens[4]); if (requiredPatientEquipmentTokens.length != equipmentListSize) { throw new IllegalArgumentException("Read line (" + line + ") is expected to contain equal number of tokens (" + requiredPatientEquipmentTokens.length + ") as equipmentListSize (" + equipmentListSize + ") after 4th pipeline (|)."); } List<RequiredPatientEquipment> requiredPatientEquipmentOfPatientList = new ArrayList<RequiredPatientEquipment>(equipmentListSize); for (int j = 0; j < requiredPatientEquipmentTokens.length; j++) { int hasEquipment = Integer.parseInt(requiredPatientEquipmentTokens[j]); if (hasEquipment == 1) { RequiredPatientEquipment requiredPatientEquipment = new RequiredPatientEquipment(); requiredPatientEquipment.setId(requiredPatientEquipmentId); requiredPatientEquipment.setPatient(patient); requiredPatientEquipment.setEquipment(indexToEquipmentMap.get(j)); requiredPatientEquipmentOfPatientList.add(requiredPatientEquipment); requiredPatientEquipmentList.add(requiredPatientEquipment); requiredPatientEquipmentId++; } else if (hasEquipment != 0) { throw new IllegalArgumentException("Read line (" + line + ") is expected to have 0 or 1 hasEquipment (" + hasEquipment + ")."); } } patient.setRequiredPatientEquipmentList(requiredPatientEquipmentOfPatientList); String[] preferredPatientEquipmentTokens = splitBySpace(lineTokens[5]); if (preferredPatientEquipmentTokens.length != equipmentListSize) { throw new IllegalArgumentException("Read line (" + line + ") is expected to contain equal number of tokens (" + preferredPatientEquipmentTokens.length + ") as equipmentListSize (" + equipmentListSize + ") after 5th pipeline (|)."); } List<PreferredPatientEquipment> preferredPatientEquipmentOfPatientList = new ArrayList<PreferredPatientEquipment>(equipmentListSize); for (int j = 0; j < preferredPatientEquipmentTokens.length; j++) { int hasEquipment = Integer.parseInt(preferredPatientEquipmentTokens[j]); if (hasEquipment == 1) { boolean alreadyRequired = (Integer.parseInt(requiredPatientEquipmentTokens[j]) == 1); // Official spec: if equipment is required // then a duplicate preffered constraint should be ignored if (!alreadyRequired) { PreferredPatientEquipment preferredPatientEquipment = new PreferredPatientEquipment(); preferredPatientEquipment.setId(preferredPatientEquipmentId); preferredPatientEquipment.setPatient(patient); preferredPatientEquipment.setEquipment(indexToEquipmentMap.get(j)); preferredPatientEquipmentOfPatientList.add(preferredPatientEquipment); preferredPatientEquipmentList.add(preferredPatientEquipment); preferredPatientEquipmentId++; } } else if (hasEquipment != 0) { throw new IllegalArgumentException("Read line (" + line + ") is expected to have 0 or 1 hasEquipment (" + hasEquipment + ")."); } } patient.setPreferredPatientEquipmentList(preferredPatientEquipmentOfPatientList); } patientAdmissionSchedule.setPatientList(patientList); patientAdmissionSchedule.setAdmissionPartList(admissionPartList); patientAdmissionSchedule.setRequiredPatientEquipmentList(requiredPatientEquipmentList); patientAdmissionSchedule.setPreferredPatientEquipmentList(preferredPatientEquipmentList); } // /** // * hack to make sure there are enough nights // * @param lastNightIndex >= 0 // */ // private void ensureEnoughNights(int lastNightIndex) { // List<Night> nightList = patientAdmissionSchedule.getNightList(); // if (lastNightIndex >= nightList.size()) { // long nightId = nightList.size(); // for (int j = nightList.size(); j <= lastNightIndex; j++) { // Night night = new Night(); // night.setId(nightId); // night.setIndex(j); // nightList.add(night); // indexToNightMap.put(j, night); // nightId++; // } // } // } // ************************************************************************ // Helper methods // ************************************************************************ private String[] splitBySpace(String line) { String[] lineTokens = line.split("\\ "); return lineTokens; } private String[] splitBySpace(String line, int numberOfTokens) { String[] lineTokens = line.split("\\ "); if (lineTokens.length != numberOfTokens) { throw new IllegalArgumentException("Read line (" + line + ") is expected to contain " + numberOfTokens + " tokens seperated by a space ( )."); } return lineTokens; } private String[] splitByPipeline(String line, int numberOfTokens) { String[] lineTokens = line.split("\\|"); if (lineTokens.length != numberOfTokens) { throw new IllegalArgumentException("Read line (" + line + ") is expected to contain " + numberOfTokens + " tokens seperated by a pipeline (|)."); } for (int i = 0; i < lineTokens.length; i++) { lineTokens[i] = lineTokens[i].trim(); } return lineTokens; } } }