Пример #1
0
 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
Пример #2
0
 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))
Пример #3
0
 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)
Пример #4
0
 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))
     ])
Пример #5
0
    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))
Пример #6
0
		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)
Пример #7
0
    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)
Пример #8
0
		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)
Пример #9
0
	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)
Пример #10
0
	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) )
Пример #11
0
	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)
Пример #12
0
 def anatomy(self, anatomy):
     self.__anatomy = Anatomy(anatomy)
Пример #13
0
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)
Пример #14
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))