def setUpClass(self): self.beams = 50 self.anatomy = Anatomy([ Structure(0, 'PTV', True, A=np.random.rand(100, self.beams)), Structure(1, 'OAR1', False, A=np.random.rand(300, self.beams)), Structure(2, 'OAR2', False, A=np.random.rand(250, self.beams)) ]) self.anatomy['PTV'].constraints += D(80) > 40 * Gy self.anatomy['PTV'].constraints += D(20) < 45 * Gy
def setUpClass(self): self.m, self.n = m, n = 500, 100 self.m_target = m_target = 100 self.m_oar = m_oar = m - m_target self.anatomy = Anatomy() self.anatomy += Structure(0, 'tumor', True, A=np.random.rand(m_target, n)) self.anatomy += Structure(1, 'oar', True, A=np.random.rand(m_oar, n))
def setUpClass(self): self.beams = 50 self.anatomy = Anatomy([ Structure(0, 'PTV', True, A=np.random.rand(100, self.beams)), Structure(1, 'OAR1', False, A=np.random.rand(300, self.beams)), Structure(2, 'OAR2', False, A=np.random.rand(250, self.beams)) ]) self.panel_assignments = {0: 0, 1: 0, 2: 0} self.names = {0: 'PTV', 1: 'OAR1', 2: 'OAR2'} self.case = Case(anatomy=self.anatomy)
def setUpClass(self): self.beams = 50 self.anatomy = Anatomy([ Structure(0, 'PTV', True, A=np.random.rand(100, self.beams)), Structure(1, 'OAR1', False, A=np.random.rand(300, self.beams)), Structure(2, 'OAR2', False, A=np.random.rand(250, self.beams)) ])
class PlanDVHGraphTestCase(ConradTestCase): @classmethod def setUpClass(self): self.beams = 50 self.anatomy = Anatomy([ Structure(0, 'PTV', True, A=np.random.rand(100, self.beams)), Structure(1, 'OAR1', False, A=np.random.rand(300, self.beams)), Structure(2, 'OAR2', False, A=np.random.rand(250, self.beams)) ]) self.anatomy['PTV'].constraints += D(80) > 40 * Gy self.anatomy['PTV'].constraints += D(20) < 45 * Gy def test_plandvh_graph_init(self): # explicit initialization pd = self.anatomy.plotting_data() pdg = PlanDVHGraph(pd) self.assertIsInstance(pdg.structure_DVHs, dict) for structure in self.anatomy: self.assertIn(structure.label, pdg.structure_DVHs) self.assertIn(structure.label, pdg.structure_labels) self.assertIn(structure.label, pdg.structure_colors) self.assertIsInstance(pdg[structure.label], StructureDVHGraph) for _, s_dvh in pdg: self.assertIsInstance(s_dvh, StructureDVHGraph) # implicit initialization pdg = PlanDVHGraph(self.anatomy) for structure in self.anatomy: self.assertIsInstance(pdg[structure.label], StructureDVHGraph) for _, s_dvh in pdg: self.assertIsInstance(s_dvh, StructureDVHGraph) def test_plandvh_graph_maxdose(self): self.anatomy.calculate_doses(np.random.rand(self.beams)) pd = self.anatomy.plotting_data() dmax = dmaxdc = 0 for label in pd: sdata = pd[label] dmax = max(dmax, sdata['curve']['dose'][-1]) dmaxdc = max(dmaxdc, dmax) for _, constr in sdata['constraints']: dmaxdc = max(dmaxdc, max(constr['dose'])) pdg = PlanDVHGraph(pd) self.assertEqual(dmaxdc, pdg.maxdose()) self.assertEqual(dmax, pdg.maxdose(exclude_constraints=True))
def setUpClass(self): self.beams = 50 self.anatomy = Anatomy([ Structure( 0, 'PTV', True, A=np.random.rand(100, self.beams)), Structure( 1, 'OAR1', False, A=np.random.rand(300, self.beams)), Structure( 2, 'OAR2', False, A=np.random.rand(250, self.beams)) ]) self.anatomy['PTV'].constraints += D(80) > 25 * Gy self.anatomy['PTV'].constraints += D(30) < 28 * Gy self.anatomy['OAR1'].constraints += D(20) < 10 * Gy self.panels = {s.label: 0 for s in self.anatomy} self.names = {s.label: s.name for s in self.anatomy} self.case = Case(anatomy=self.anatomy)
class PlanConstraintsGraphTestCase(ConradTestCase): @classmethod def setUpClass(self): self.beams = 50 self.anatomy = Anatomy([ Structure(0, 'PTV', True, A=np.random.rand(100, self.beams)), Structure(1, 'OAR1', False, A=np.random.rand(300, self.beams)), Structure(2, 'OAR2', False, A=np.random.rand(250, self.beams)) ]) self.anatomy['PTV'].constraints += D(80) > 40 * Gy self.anatomy['PTV'].constraints += D(20) < 45 * Gy def test_plan_constraints_graph_init(self): # # explicit initialization pd = self.anatomy.plotting_data(constraints_only=True) pcg = PlanConstraintsGraph(pd) self.assertIsInstance(pcg.structure_constraints, dict) for structure in self.anatomy: self.assertIn(structure.label, pcg.structure_constraints) self.assertIn(structure.label, pcg.structure_labels) self.assertIn(structure.label, pcg.structure_colors) self.assertIsInstance(pcg[structure.label], StructureConstraintsGraph) for _, s_constr in pcg: self.assertIsInstance(s_constr, StructureConstraintsGraph) # implicit initialization pcg = PlanConstraintsGraph(self.anatomy) for structure in self.anatomy: self.assertIsInstance(pcg[structure.label], StructureConstraintsGraph) for _, s_constr in pcg: self.assertIsInstance(s_constr, StructureConstraintsGraph) def test_plan_constraints_graph_maxdose(self): self.anatomy.calculate_doses(np.random.rand(self.beams)) pd = self.anatomy.plotting_data(constraints_only=True) dmax = 0 for label in pd: for _, constr in pd[label]: dmax = max(dmax, max(constr['dose'])) pcg = PlanConstraintsGraph(pd) self.assertEqual(dmax, pcg.maxdose)
def setUpClass(self): self.beams = 50 self.anatomy = Anatomy([ Structure(0, 'PTV', True, A=rand(100, self.beams)), Structure(1, 'OAR1', False, A=rand(300, self.beams)), Structure(2, 'OAR2', False, A=rand(250, self.beams)) ]) self.panels = {0: 1, 1: 1, 2: 1} self.names = {0: 'PTV', 1: 'OAR1', 2: 'OAR2'} self.case = Case(anatomy=self.anatomy)
def setUpClass(self): self.m_target = 100 self.m_oar = 400 self.m = self.m_target + self.m_oar self.n = 207 # Structure labels self.label_tumor = 0 self.label_oar = 1 # Voxel labels on beam matrix self.label_order = [self.label_tumor, self.label_oar] self.voxel_labels = [self.label_tumor] * self.m_target self.voxel_labels += [self.label_oar] * self.m_oar self.anatomy = Anatomy() self.anatomy += Structure(self.label_tumor, 'tumor', True) self.anatomy += Structure(self.label_oar, 'oar', False)
class VisualizationTestCase(ConradTestCase): @classmethod def setUpClass(self): self.beams = 50 self.anatomy = Anatomy([ Structure(0, 'PTV', True, A=rand(100, self.beams)), Structure(1, 'OAR1', False, A=rand(300, self.beams)), Structure(2, 'OAR2', False, A=rand(250, self.beams)) ]) self.panels = {0: 1, 1: 1, 2: 1} self.names = {0: 'PTV', 1: 'OAR1', 2: 'OAR2'} self.case = Case(anatomy=self.anatomy) def test_panels_to_cols(self): self.assertTrue( panels_to_cols(1) == 1 ) self.assertTrue( panels_to_cols(2) == 2 ) self.assertTrue( panels_to_cols(3) == 2 ) self.assertTrue( panels_to_cols(4) == 2 ) self.assertTrue( panels_to_cols(5) == 3 ) self.assertTrue( panels_to_cols(6) == 3 ) self.assertTrue( panels_to_cols(7) == 4 ) self.assertTrue( panels_to_cols(8) == 4 ) self.assertTrue( panels_to_cols(12) == 4 ) self.assertTrue( panels_to_cols(15) == 4 ) self.assertTrue( panels_to_cols(25) == 4 ) def test_dvh_plot_init(self): d = DVHPlot(self.panels, self.names) if d is None: return self.assertTrue( isinstance(d.fig, Figure) ) self.assertTrue( d.n_structures == 3 ) self.assertTrue( d._DVHPlot__panels_by_structure == self.panels ) self.assertTrue( d.n_panels == 1 ) self.assertTrue( d.cols == 1 ) self.assertTrue( d.rows == 1 ) self.assertTrue( d._DVHPlot__names_by_structure == self.names ) self.assertTrue( isinstance(d._DVHPlot__colors_by_structure, dict) ) self.assertTrue( len(d._DVHPlot__colors_by_structure) == 3 ) def test_dvh_plot_plot(self): d = DVHPlot(self.panels, self.names) if d is None: return self.anatomy.calculate_doses(rand(self.beams)) d.plot(self.anatomy.plotting_data) self.assertTrue( isinstance(d.fig, Figure) ) # TODO: test options def test_dvh_plot_save(self): d = DVHPlot(self.panels, self.names) if d is None: return filename = path.join(path.abspath(path.dirname(__file__)), 'test.pdf') self.assertFalse( path.exists(filename) ) d.save(filename) self.assertTrue( path.exists(filename) ) os_remove(filename) self.assertFalse( path.exists(filename) ) def test_case_plotter_init(self): cp = CasePlotter(self.case) if cp is None: return self.assertTrue( isinstance(cp.dvh_plot, DVHPlot) ) label_list = [0, 1, 2, 'PTV', 'OAR1', 'OAR2'] self.assertTrue( all( [cp.label_is_valid(label) for label in label_list]) ) def test_case_plotter_set_display_groups(self): self.case.anatomy.label_order = [0, 1, 2] cp = CasePlotter(self.case) if cp is None: return cp.set_display_groups('together') self.assert_vector_equal( vec(list(cp.dvh_plot.series_panels.values())), vec([1, 1, 1]) ) cp.set_display_groups('separate') self.assert_vector_equal( vec(list(cp.dvh_plot.series_panels.values())), vec([1, 2, 3]) ) cp.set_display_groups('list', [('PTV',), ('OAR1', 'OAR2')]) self.assert_vector_equal( vec(list(cp.dvh_plot.series_panels.values())), vec([1, 2, 2]) ) def test_case_plotter_plot(self): cp = CasePlotter(self.case) if cp is None: return self.anatomy.calculate_doses(rand(self.beams)) cp.plot(self.anatomy.plotting_data) self.assertTrue( isinstance(cp.dvh_plot.fig, Figure) )
class CasePlotterTestCase(ConradTestCase): @classmethod def setUpClass(self): self.beams = 50 self.anatomy = Anatomy([ Structure( 0, 'PTV', True, A=np.random.rand(100, self.beams)), Structure( 1, 'OAR1', False, A=np.random.rand(300, self.beams)), Structure( 2, 'OAR2', False, A=np.random.rand(250, self.beams)) ]) self.anatomy['PTV'].constraints += D(80) > 25 * Gy self.anatomy['PTV'].constraints += D(30) < 28 * Gy self.anatomy['OAR1'].constraints += D(20) < 10 * Gy self.panels = {s.label: 0 for s in self.anatomy} self.names = {s.label: s.name for s in self.anatomy} self.case = Case(anatomy=self.anatomy) def setUp(self): self.anatomy.calculate_doses(np.random.rand(self.beams)) # store function in cases when it is disabled self.autoset_colors = CasePlotter.autoset_structure_colors def tearDown(self): CasePlotter.autoset_structure_colors = self.autoset_colors def test_caseplotter_init_properties(self): # disable autoset colors in initialization CasePlotter.autoset_structure_colors = lambda arg_self: None with self.assertRaises(TypeError): cp = CasePlotter('not a case') # standard initialization cp = CasePlotter(self.case) n_struc = self.case.anatomy.n_structures self.assertIsInstance( cp.dvh_plot, DVHPlot ) self.assertIsNone( cp.dvh_set ) self.assertIsInstance( cp.structure_subset, list ) self.assertEqual( len(cp.structure_subset), n_struc ) self.assertIsInstance( cp.structure_colors, dict ) self.assertEqual( len(cp.structure_colors), 0 ) self.assertIsInstance( cp._CasePlotter__tag2label, dict ) self.assertEqual( len(cp._CasePlotter__tag2label), 2 * n_struc ) self.assertEqual( cp.grouping, 'together' ) self.assertEqual( cp.n_structures, self.anatomy.n_structures ) tags = [s.label for s in self.anatomy] tags += [s.name for s in self.anatomy] self.assertTrue( all( [t in cp._CasePlotter__tag2label for t in tags]) ) # subset filtering test_list = [0, 1, 2, 3, 4, 'PTV', 'OAR', 'OAR1', 'some string'] filtered_list = cp.filter_labels(test_list) self.assertSetEqual( set(filtered_list), {i for i in xrange(n_struc)} ) test_dictionary = {item: item for item in test_list} filtered_dict = cp.filter_data(test_dictionary) self.assertDictEqual( filtered_dict, {i: i for i in xrange(n_struc)} ) # add data cp.dvh_set = self.case.anatomy.plotting_data() self.assertIsInstance( cp.dvh_set, PlanDVHGraph ) self.assertEqual( len(cp.dvh_set.structure_DVHs), n_struc ) # subset initialization subset1 = [0, 1] n_subset = len(subset1) cp = CasePlotter(self.case, subset=subset1) self.assertEqual( len(cp.structure_subset), n_subset ) self.assertEqual( len(cp._CasePlotter__tag2label), 2 * n_struc ) self.assertEqual( cp.n_structures, n_subset ) cp.dvh_set = self.case.anatomy.plotting_data() self.assertEqual( len(cp.dvh_set.structure_DVHs), n_subset ) subset2 = [self.names[label] for label in subset1] cp = CasePlotter(self.case, subset=subset2) self.assertEqual( len(cp.structure_subset), n_subset ) self.assertEqual( len(cp._CasePlotter__tag2label), 2 * n_struc ) self.assertEqual( cp.n_structures, n_subset ) cp.dvh_set = self.case.anatomy.plotting_data() self.assertEqual( len(cp.dvh_set.structure_DVHs), n_subset ) # verify that redundant entries are included once subset3 = subset1 + subset2 cp = CasePlotter(self.case, subset=subset3) self.assertEqual( len(cp.structure_subset), n_subset ) self.assertEqual( len(cp._CasePlotter__tag2label), 2 * n_struc ) self.assertEqual( cp.n_structures, n_subset ) cp.dvh_set = self.case.anatomy.plotting_data() self.assertEqual( len(cp.dvh_set.structure_DVHs), n_subset ) def test_caseplotter_colors(self): # disable autoset colors in initialization CasePlotter.autoset_structure_colors = lambda arg_self: None cp = CasePlotter(self.case) colors = {0: 'blue', 1: 'red', 2:'yellow'} colors_snames = {'PTV': 'blue', 'OAR1': 'red', 'OAR2':'yellow'} cp.structure_colors = colors self.assertEqual( cp.structure_colors, colors ) cp.structure_colors = colors_snames self.assertEqual( cp.structure_colors, colors ) colors[0] = 'chartreuse' cp.structure_colors = {0: 'chartreuse'} self.assertEqual( cp.structure_colors, colors ) # restore autoset colors CasePlotter.autoset_structure_colors = self.autoset_colors # test autoset_structure_colors cp._CasePlotter__structure_colors = {} self.assertEqual( len(cp.structure_colors), 0 ) cp.autoset_structure_colors() self.assertEqual( len(cp.structure_colors), cp.n_structures ) # autoset with structure order permutation order0 = [0, 1, 2] order1 = [2, 0, 1] colors_permuted = {label: cp.structure_colors[order0[i]] for i, label in enumerate(order1)} cp.autoset_structure_colors(structure_order=order1) self.assertEqual( colors_permuted, cp.structure_colors ) # autoset with invalid structure order permutation with self.assertRaises(ValueError): cp.autoset_structure_colors(structure_order=[2, 0]) # autoset with non-default colormap cp._CasePlotter__structure_colors = {} cp.autoset_structure_colors(colormap='rainbow') self.assertEqual( len(cp.structure_colors), cp.n_structures ) # autoset with invalid colormap with self.assertRaises(ValueError): cp.autoset_structure_colors(colormap='garbage string') def test_caseplotter_grouping(self): self.case.anatomy.label_order = [0, 1, 2] cp = CasePlotter(self.case) cp.grouping = 'together' self.assertEqual( cp.grouping, 'together' ) for i in xrange(3): self.assertEqual( cp.dvh_plot.subplot_assignments[i], 0 ) cp.grouping = 'separate' self.assertEqual( cp.grouping, 'separate' ) for i in xrange(3): self.assertEqual( cp.dvh_plot.subplot_assignments[i], i ) group_alternates = [ (0, (1, 2)), (0, [1, 2]), [0, (1, 2)], [0, [1, 2]]] group_alternates += [ ('PTV', ('OAR1', 'OAR2')), ('PTV', [1, 'OAR2'])] for g in group_alternates: cp.grouping = g for i in xrange(3): self.assertEqual( cp.grouping, 'list' ) self.assertEqual( cp.dvh_plot.subplot_assignments[i], int(i > 0) ) with self.assertRaises(TypeError): cp.grouping = 1 with self.assertRaises(ValueError): cp.grouping = 'not a valid grouping' with self.assertRaises(ValueError): cp.grouping = [0, 1] def test_caseplotter_plot(self): cp = CasePlotter(self.case) plan_data = self.case.plotting_data(x=np.random.rand(self.beams)) cp.plot(plan_data) # # TODO: test options def test_caseplotter_twopass(self): cp = CasePlotter(self.case) plan_data_pass1 = self.case.plotting_data( x=np.random.rand(self.beams)) plan_data_pass2 = self.case.plotting_data( x=np.random.rand(self.beams)) cp.plot_twopass([plan_data_pass1, plan_data_pass2]) # # TODO: test options def test_caseplotter_plotmulti(self): cp = CasePlotter(self.case) plan_data_ref = self.case.plotting_data( x=np.random.rand(self.beams)) plan_data_list = [ self.case.plotting_data(x=np.random.rand(self.beams)) for i in xrange(3)] plan_names = ['first', 'second', 'third'] cp.plot_multi(plan_data_list, plan_names) cp.plot_multi(plan_data_list, plan_names, reference_data=plan_data_ref)
def anatomy(self, anatomy): self.__anatomy = Anatomy(anatomy)
class PlanningProblemTestCase(ConradTestCase): @classmethod def setUpClass(self): self.m, self.n = m, n = 500, 100 self.m_target = m_target = 100 self.m_oar = m_oar = m - m_target self.anatomy = Anatomy() self.anatomy += Structure(0, 'tumor', True, A=np.random.rand(m_target, n)) self.anatomy += Structure(1, 'oar', True, A=np.random.rand(m_oar, n)) def tearDown(self): for struc in self.anatomy: struc.constraints.clear() def test_problem_init(self): p = PlanningProblem() if module_installed('cvxpy'): self.assertIsNotNone(p.solver_cvxpy) else: self.assertIsNone(p.solver_cvxpy) if module_installed('optkit'): self.assertIsNotNone(p.solver_pogs) else: self.assertIsNone(p.solver_pogs) self.assertIsNone(p._PlanningProblem__solver) self.assertIsNotNone(p.solver) def test_updates(self): p = PlanningProblem() struc = self.anatomy['tumor'] struc.constraints += D(30) < 30 * Gy # CVXPY if p.solver_cvxpy is not None: p._PlanningProblem__solver = p.solver_cvxpy p.solver_cvxpy.init_problem(self.n) p.solver_cvxpy.build(self.anatomy.list) p.solver_cvxpy.solve(verbose=0) x_optimal = p.solver_cvxpy.x p._PlanningProblem__update_constraints(struc) for key in struc.constraints: self.assertTrue(struc.constraints[key].slack >= 0) p._PlanningProblem__update_structure(struc) self.assert_vector_equal(struc.A.dot(x_optimal), struc.y) # OPKIT if p.solver_pogs is not None: self.anatomy.clear_constraints() p._PlanningProblem__solver = p.solver_pogs x_rand = np.random.rand(self.n) p.solver_pogs.build(self.anatomy.list) p.solver_pogs.pogs_solver.output.x[:] = x_rand p._PlanningProblem__update_constraints(struc) for key in struc.constraints: self.assertTrue(struc.constraints[key].slack == 0) p._PlanningProblem__update_structure(struc) self.assert_vector_equal(struc.A.dot(x_rand), struc.y) def test_gather_solver_data(self): p = PlanningProblem() # CVXPY if p.solver_cvxpy is not None: p._PlanningProblem__solver = p.solver_cvxpy self.anatomy['tumor'].constraints += D(90) > 0.5 * Gy p.solver_cvxpy.init_problem(self.n) p.solver_cvxpy.build(self.anatomy.list) p.solver_cvxpy.solve(verbose=0) ro = RunOutput() # gather solver info p._PlanningProblem__gather_solver_info(ro) self.assertEqual(ro.solver_info['status'], p.solver.status) self.assertEqual(ro.solver_info['time'], p.solver.solvetime) self.assertEqual(ro.solver_info['objective'], p.solver.objective_value) self.assertEqual(ro.solver_info['iters'], p.solver.solveiters) # gather solver info, exact p._PlanningProblem__gather_solver_info(ro, exact=True) self.assertEqual(ro.solver_info['status_exact'], p.solver.status) self.assertEqual(ro.solver_info['time_exact'], p.solver.solvetime) self.assertEqual(ro.solver_info['objective_exact'], p.solver.objective_value) self.assertEqual(ro.solver_info['iters_exact'], p.solver.solveiters) # gather solver variables p._PlanningProblem__gather_solver_vars(ro) self.assert_vector_equal(ro.optimal_variables['x'], p.solver.x) self.assert_vector_equal(ro.optimal_variables['mu'], p.solver.x_dual) self.assertIsNone(ro.optimal_variables['nu']) # gather solver variables, exact p._PlanningProblem__gather_solver_vars(ro, exact=True) self.assert_vector_equal(ro.optimal_variables['x_exact'], p.solver.x) self.assert_vector_equal(ro.optimal_variables['mu_exact'], p.solver.x_dual) self.assertIsNone(ro.optimal_variables['nu_exact']) # gather DVH slopes p._PlanningProblem__gather_dvh_slopes(ro, self.anatomy.list) self.assertTrue( all([slope > 0 for slope in ro.optimal_dvh_slopes.values()])) # OPKIT if p.solver_pogs is not None: self.anatomy.clear_constraints() p._PlanningProblem__solver = p.solver_pogs p.solver.build(self.anatomy.list) p.solver.solve(verbose=0) ro = RunOutput() # gather solver info p._PlanningProblem__gather_solver_info(ro) self.assertEqual(ro.solver_info['status'], p.solver.status) self.assertEqual(ro.solver_info['time'], p.solver.solvetime) self.assertEqual(ro.solver_info['objective'], p.solver.objective_value) self.assertEqual(ro.solver_info['iters'], p.solver.solveiters) # gather solver info, exact p._PlanningProblem__gather_solver_info(ro, exact=True) self.assertEqual(ro.solver_info['status_exact'], p.solver.status) self.assertEqual(ro.solver_info['time_exact'], p.solver.solvetime) self.assertEqual(ro.solver_info['objective_exact'], p.solver.objective_value) self.assertEqual(ro.solver_info['iters_exact'], p.solver.solveiters) # gather solver variables p._PlanningProblem__gather_solver_vars(ro) self.assert_vector_equal(ro.optimal_variables['x'], p.solver.x) self.assert_vector_equal(ro.optimal_variables['mu'], p.solver.x_dual) self.assert_vector_equal(ro.optimal_variables['nu'], p.solver.y_dual) # gather solver variables, exact p._PlanningProblem__gather_solver_vars(ro, exact=True) self.assert_vector_equal(ro.optimal_variables['x_exact'], p.solver.x) self.assert_vector_equal(ro.optimal_variables['mu_exact'], p.solver.x_dual) self.assert_vector_equal(ro.optimal_variables['nu_exact'], p.solver.y_dual) # gather DVH slopes p._PlanningProblem__gather_dvh_slopes(ro, self.anatomy.list) self.assertTrue( all([slope is nan for slope in ro.optimal_dvh_slopes.values()])) def test_fastest_solver(self): p = PlanningProblem() # unconstrained problem: OPTKIT is fastest, if available p._PlanningProblem__set_solver_fastest_available(self.anatomy.list) if p.solver_pogs is not None: self.assertEqual(p.solver, p.solver_pogs) elif p.solver_cvxpy is not None: self.assertEqual(p.solver, p.solver_cvxpy) self.anatomy['tumor'].constraints += D('mean') < 15 * Gy p._PlanningProblem__set_solver_fastest_available(self.anatomy.list) if p.solver_cvxpy is not None: self.assertEqual(p.solver, p.solver_cvxpy) # constrained problem: CVXPY is only solver that can handle it self.anatomy['tumor'].constraints -= self.anatomy[ 'tumor'].constraints.last_key self.anatomy['tumor'].constraints += D('min') > 5 * Gy p._PlanningProblem__set_solver_fastest_available(self.anatomy.list) if p.solver_cvxpy is not None: self.assertEqual(p.solver, p.solver_cvxpy) self.anatomy['tumor'].constraints -= self.anatomy[ 'tumor'].constraints.last_key self.anatomy['tumor'].constraints += D('max') < 20 * Gy p._PlanningProblem__set_solver_fastest_available(self.anatomy.list) if p.solver_cvxpy is not None: self.assertEqual(p.solver, p.solver_cvxpy) self.anatomy['tumor'].constraints -= self.anatomy[ 'tumor'].constraints.last_key self.anatomy['tumor'].constraints += D(2) < 20 * Gy p._PlanningProblem__set_solver_fastest_available(self.anatomy.list) if p.solver_cvxpy is not None: self.assertEqual(p.solver, p.solver_cvxpy) self.anatomy['tumor'].constraints -= self.anatomy[ 'tumor'].constraints.last_key def test_verify_2pass(self): p = PlanningProblem() self.anatomy['tumor'].constraints += D('mean') > 10 * Gy self.assertFalse( p._PlanningProblem__verify_2pass_applicable(self.anatomy.list)) self.anatomy['tumor'].constraints += D('mean') < 15 * Gy self.assertFalse( p._PlanningProblem__verify_2pass_applicable(self.anatomy.list)) self.anatomy['tumor'].constraints += D('min') > 5 * Gy self.assertFalse( p._PlanningProblem__verify_2pass_applicable(self.anatomy.list)) self.anatomy['tumor'].constraints += D('max') < 20 * Gy self.assertFalse( p._PlanningProblem__verify_2pass_applicable(self.anatomy.list)) self.anatomy['tumor'].constraints += D(2) < 20 * Gy self.assertTrue( p._PlanningProblem__verify_2pass_applicable(self.anatomy.list)) def test_solve(self): p = PlanningProblem() # force exception p.solver_cvxpy = None p.solver_pogs = None ro = RunOutput() with self.assertRaises(ValueError): p.solve(self.anatomy.list, ro) p = PlanningProblem() # unconstrained, no slack (slack irrelevant) # - return code = 1 (1 feasible problem solved) ro = RunOutput() feasible = p.solve(self.anatomy.list, ro, slack=False, verbose=0) self.assertEqual(feasible, 1) self.assertGreater(ro.solvetime, 0) self.assert_nan(ro.solvetime_exact) # constrained, no slack # - return code = 1 ro = RunOutput() self.anatomy['tumor'].constraints += D('mean') > 10 * Gy cid1 = self.anatomy['tumor'].constraints.last_key feasible = p.solve(self.anatomy.list, ro, slack=False, verbose=0) self.assertEqual(feasible, 1) self.assertGreater(ro.solvetime, 0) self.assert_nan(ro.solvetime_exact) # add infeasible constraint # - return code = 0 (0 feasible problems solved) ro = RunOutput() self.anatomy['tumor'].constraints += D('mean') < 8 * Gy feasible = p.solve(self.anatomy.list, ro, slack=False, verbose=0) self.assertEqual(feasible, 0) self.assertGreater(ro.solvetime, 0) self.assert_nan(ro.solvetime_exact) self.anatomy['tumor'].constraints -= self.anatomy[ 'tumor'].constraints.last_key # no slack, 2-pass # - no DVH constraints: request but don't perform 2-pass # - return code = 1 ro = RunOutput() feasible = p.solve(self.anatomy.list, ro, slack=False, verbose=0, exact_constraints=True) self.assertEqual(feasible, 1) self.assertGreater(ro.solvetime, 0) self.assert_nan(ro.solvetime_exact) # - +DVH constraint: request *and* perform 2-pass method # - return code = 2 (2 feasible problems solved) ro = RunOutput() self.anatomy['tumor'].constraints += D(10) < 20 * Gy cid2 = self.anatomy['tumor'].constraints.last_key feasible = p.solve(self.anatomy.list, ro, slack=False, verbose=0, exact_constraints=True) self.assertEqual(feasible, 2) self.assertGreater(ro.solvetime, 0) self.assertGreater(ro.solvetime_exact, 0) # slack # - infeasible constraint, problem feasible with slack # (on percentile constraints) # - return code = 1 ro = RunOutput() self.anatomy['tumor'].constraints += D(50) < 8 * Gy cid3 = self.anatomy['tumor'].constraints.last_key self.anatomy['tumor'].constraints[cid1].priority = 0 self.anatomy['tumor'].constraints[cid2].priority = 1 self.anatomy['tumor'].constraints[cid3].priority = 1 feasible = p.solve(self.anatomy.list, ro, slack=True, verbose=0) self.assertEqual(feasible, 1) self.assertGreater(ro.solvetime, 0) self.assert_nan(ro.solvetime_exact) # slack, 2-pass # - return code = 2 ro = RunOutput() feasible = p.solve(self.anatomy.list, ro, slack=True, verbose=0, exact_constraints=True) self.assertEqual(feasible, 2) self.assertGreater(ro.solvetime, 0) self.assertGreater(ro.solvetime_exact, 0)
class DVHPlotTestCase(ConradTestCase): @classmethod def setUpClass(self): self.beams = 50 self.anatomy = Anatomy([ Structure(0, 'PTV', True, A=np.random.rand(100, self.beams)), Structure(1, 'OAR1', False, A=np.random.rand(300, self.beams)), Structure(2, 'OAR2', False, A=np.random.rand(250, self.beams)) ]) self.panel_assignments = {0: 0, 1: 0, 2: 0} self.names = {0: 'PTV', 1: 'OAR1', 2: 'OAR2'} self.case = Case(anatomy=self.anatomy) def setUp(self): self.build_call = DVHPlot.build def tearDown(self): DVHPlot.build = self.build_call def test_dvhplot_init_properties(self): DVHPlot.build = lambda arg_self: None d = DVHPlot(self.panel_assignments) self.assertNotIsInstance(d.figure, mpl.figure.Figure) self.assertEqual(d.n_structures, 3) self.assertIsInstance(d.subplots, dict) self.assertEqual(len(d.subplots), 0) self.assertIsInstance(d.panels, list) self.assertEqual(len(d.panels), 0) for k, v in d.subplot_assignments.items(): self.assertEqual(self.panel_assignments[k], v) self.assertEqual(d.cols, 1) self.assertEqual(d.rows, 1) self.assertEqual(d.layout, 'auto') def test_dvhplot_panels_to_cols(self): DVHPlot.build = lambda arg_self: None d = DVHPlot(self.panel_assignments) self.assertEqual(d.subplots_to_cols(1), 1) self.assertEqual(d.subplots_to_cols(2), 2) self.assertEqual(d.subplots_to_cols(3), 2) self.assertEqual(d.subplots_to_cols(4), 2) self.assertEqual(d.subplots_to_cols(5), 3) self.assertEqual(d.subplots_to_cols(6), 3) self.assertEqual(d.subplots_to_cols(7), 4) self.assertEqual(d.subplots_to_cols(8), 4) self.assertEqual(d.subplots_to_cols(12), 4) self.assertEqual(d.subplots_to_cols(15), 4) self.assertEqual(d.subplots_to_cols(25), 4) def test_dvhplot_sift_options(self): DVHPlot.build = lambda arg_self: None d = DVHPlot(self.panel_assignments) options = { 'legend_opt1': 1, 'legend_opt2': 2, 'other_opt1': 3, 'other_opt2': 4 } legend_options = {'opt1': 1, 'opt2': 2} other_options = {'other_opt1': 3, 'other_opt2': 4} o_opt, l_opt = d.sift_options(**options) for k, v in legend_options.items(): self.assertEqual(l_opt[k], v) for k, v in other_options.items(): self.assertEqual(o_opt[k], v) def test_dvhplot_build_clear(self): DVHPlot.build = lambda arg_self: None d = DVHPlot(self.panel_assignments) self.assertNotIsInstance(d.figure, mpl.figure.Figure) self.assertIsInstance(d.subplots, dict) self.assertEqual(len(d.subplots), 0) self.assertIsInstance(d.panels, list) self.assertEqual(len(d.panels), 0) # test calculate panels d.calculate_panels() self.assertEqual(d.n_subplots, 1) self.assertEqual(d.rows, 1) self.assertEqual(d.cols, 1) d._DVHPlot__subplot_assignments_by_structure = {0: 0, 1: 1, 2: 2} d.calculate_panels() self.assertEqual(d.n_subplots, 3) self.assertEqual(d.rows, 2) self.assertEqual(d.cols, 2) d._DVHPlot__layout = 'vertical' d.calculate_panels() self.assertEqual(d.n_subplots, 3) self.assertEqual(d.rows, 3) self.assertEqual(d.cols, 1) d._DVHPlot__layout = 'horizontal' d.calculate_panels() self.assertEqual(d.n_subplots, 3) self.assertEqual(d.rows, 1) self.assertEqual(d.cols, 3) # test build/clear DVHPlot.build = self.build_call d._DVHPlot__layout = 'auto' d.subplot_assignments = {0: 0, 1: 0, 2: 0} d.build() self.assertIsInstance(d.figure, mpl.figure.Figure) self.assertEqual(len(d.subplots), 3) for sp in d.subplots.values(): self.assertIsInstance(sp, DVHSubplot) self.assertIsInstance(d.panels, list) self.assertEqual(len(d.panels), 1) for sp in d.panels: self.assertIsInstance(sp, DVHSubplot) d.clear() self.assertNotIsInstance(d.figure, mpl.figure.Figure) self.assertIsInstance(d.subplots, dict) self.assertEqual(len(d.subplots), 0) self.assertIsInstance(d.panels, list) self.assertEqual(len(d.panels), 0) def test_dvhplot_panels_layout(self): d = DVHPlot(self.panel_assignments) self.assertIsInstance(d.figure, mpl.figure.Figure) self.assertEqual(d.n_subplots, 1) self.assertEqual(d.rows, 1) self.assertEqual(d.cols, 1) self.assertEqual(len(d.subplots), 3) self.assertEqual(len(d.panels), 1) for i in xrange(3): self.assertEqual(d.subplots[i], d.panels[0]) self.assertTrue(d.subplots[i].bottom) self.assertTrue(d.subplots[i].left) d.subplot_assignments = {0: 0, 1: 1, 2: 2} d.build() self.assertEqual(d.n_subplots, 3) self.assertEqual(d.rows, 2) self.assertEqual(d.cols, 2) self.assertEqual(len(d.subplots), 3) self.assertEqual(len(d.panels), 3) for i in xrange(3): self.assertEqual(d.subplots[i], d.panels[i]) self.assertEqual(d.subplots[i].bottom, bool(i >= d.cols)) self.assertEqual(d.subplots[i].left, bool(i % d.cols == 0)) d.layout = 'vertical' self.assertEqual(d.n_subplots, 3) self.assertEqual(d.rows, 3) self.assertEqual(d.cols, 1) self.assertEqual(len(d.subplots), 3) self.assertEqual(len(d.panels), 3) for i in xrange(3): self.assertEqual(d.subplots[i], d.panels[i]) self.assertEqual(d.subplots[i].bottom, bool(i == d.n_subplots - 1)) self.assertTrue(d.subplots[i].left) d.layout = 'horizontal' self.assertEqual(d.n_subplots, 3) self.assertEqual(d.rows, 1) self.assertEqual(d.cols, 3) self.assertEqual(len(d.subplots), 3) self.assertEqual(len(d.panels), 3) for i in xrange(3): self.assertEqual(d.subplots[i], d.panels[i]) self.assertTrue(d.subplots[i].bottom) self.assertEqual(d.subplots[i].left, bool(i == 0)) with self.assertRaises(ValueError): d.layout = 'not a valid layout string' def test_dvhplot_getitem(self): d = DVHPlot(self.panel_assignments) for key in [0, 1, 2, 'upper right']: self.assertIsInstance(d[key], DVHSubplot) self.assertEqual(d[key], d.panels[0]) d.subplot_assignments = {0: 0, 1: 1, 2: 2} d.build() for key in [0, 1, 2, 'upper right']: self.assertIsInstance(d[key], DVHSubplot) if isinstance(key, int): self.assertEqual(d[key], d.panels[key]) else: self.assertEqual(d[key], d.panels[1]) d.layout = 'horizontal' self.assertEqual(d['upper right'], d.panels[-1]) def test_dvhplot_drawlegend(self): d = DVHPlot(self.panel_assignments) series = [mpl.lines.Line2D([], []) for i in xrange(3)] names = ['first', 'second', 'third'] self.assertEqual(len(d.figure.legends), 0) d.draw_legend(series, names) self.assertEqual(len(d.figure.legends), 1) self.assertEqual(len(d.figure.legends[-1].get_lines()), len(series)) for text in d.figure.legends[-1].get_texts(): self.assertTrue(text.get_text() in names) # coordinates width = d.figure.legends[-1].get_bbox_to_anchor().x1 height = d.figure.legends[-1].get_bbox_to_anchor().y1 coords = [0.5, 0.6] d.draw_legend(series, names, coordinates=coords) origin_x = d.figure.legends[-1].get_bbox_to_anchor().x0 origin_y = d.figure.legends[-1].get_bbox_to_anchor().y0 self.assertTrue(coords[0] * width == origin_x) self.assertTrue(coords[1] * height == origin_y) # location d.draw_legend(series, names, coordinates=coords, alignment='right') d.draw_legend(series, names, coordinates=coords, alignment='center') self.assertNotEqual(d.figure.legends[-2]._loc, d.figure.legends[-1]._loc) def test_dvhplot_drawtitle(self): d = DVHPlot(self.panel_assignments) d.draw_title('test title') self.assertEqual(d.figure.texts[-1].get_text(), 'test title') def test_dvhplot_format_subplots(self): d = DVHPlot(self.panel_assignments) xmax = 50. d.format_subplots(xmax) for subplot in d.panels: self.assertEqual(subplot.xmax, xmax) def test_check_figure(self): DVHPlot.build = lambda arg_self: None d = DVHPlot(self.panel_assignments) with self.assertRaises(AttributeError): d.check_figure() DVHPlot.build = self.build_call d = DVHPlot(self.panel_assignments) d.check_figure() d.clear() with self.assertRaises(AttributeError): d.check_figure() def test_dvhplot_plotvirtual(self): d = DVHPlot(self.panel_assignments) series_names = ['first', 'second', 'third'] series_aesthetics = [LineAesthetic() for i in xrange(3)] d.plot_virtual(series_names, series_aesthetics) self.assertEqual(len(d.figure.legends), 1) self.assertEqual(len(d.figure.legends[-1].get_lines()), 3) for text in d.figure.legends[-1].get_texts(): self.assertIn(text.get_text(), series_names) def test_dvhplot_plotlabels(self): d = DVHPlot(self.panel_assignments) coords = (0.5, 0.6) d.plot_labels({1: coords}, self.anatomy.plotting_data()) self.assertEqual(len(d.subplots[1].axes.texts), 1) t = d.subplots[1].axes.texts[-1] self.assertEqual(t.get_text(), self.anatomy[1].name) self.assertEqual(t.get_unitless_position(), coords) with self.assertRaises(KeyError): d.plot_labels({4: coords}, self.anatomy.plotting_data()) def test_dvhplot_plotconstraints(self): d = DVHPlot(self.panel_assignments) LABEL = 0 self.anatomy[LABEL].constraints += D(30) < 20 * Gy pc = PlanConstraintsGraph(self.anatomy) d.plot_constraints(pc) for constr in pc[LABEL]: self.assertIs(constr.axes, d.subplots[LABEL].axes) def test_dvhplot_plot(self): d = DVHPlot({i: i for i in xrange(3)}) d.layout = 'horizontal' LABEL = 0 self.anatomy.calculate_doses(np.random.rand(self.beams)) self.anatomy[LABEL].constraints += D(30) < 20 * Gy pd = PlanDVHGraph(self.anatomy.plotting_data()) # vanilla plot + self-title subplots d.plot(self.anatomy.plotting_data(), self_title_subplots=True) for structure in self.anatomy: self.assertEqual(d.subplots[structure.label].title, structure.name) # individual legends d.plot(self.anatomy.plotting_data(), legend='each') for structure in self.anatomy: self.assertIsNotNone(d.subplots[structure.label].legend) self.assertEqual( len(d.subplots[structure.label].legend.get_lines()), 1) self.assertEqual(len(d.figure.legends), 0) # overall legend d.plot(self.anatomy.plotting_data(), legend=True) for structure in self.anatomy: self.assertIsNone(d.subplots[structure.label].legend) self.assertEqual(len(d.figure.legends), 1) # self-title for multiple structures on single subplot d.subplot_assignments = {i: 0 for i in xrange(3)} d.build() d.plot(pd, self_title_subplots=True) title = '' for label, _ in pd: title += self.anatomy[label].name title += ', ' title = title[:-2] # trim terminal ", " self.assertEqual(d.panels[0].title, title) # def test_dvhplot_show(self): # pass def test_dvhplot_save(self): d = DVHPlot(self.panel_assignments) filename = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'test.pdf') self.assertFalse(os.path.exists(filename)) d.build() d.save(filename) self.assertTrue(os.path.exists(filename)) os.remove(filename) self.assertFalse(os.path.exists(filename))