Skip to content

Conversation

@coreyostrove
Copy link
Contributor

@coreyostrove coreyostrove commented Oct 9, 2025

We recently ran into a scenario where it was desirable to perform germ selection in a manner which minimized the number of applications of a particularly expensive gate operation. To support that sort of use case this PR adds a new algorithm_kwarg option called gate_penalty for find_germs which is compatible with the 'greedy' and 'grasp' search algorithms which allows a user to specify and additional penalty factors for the number of instances of particular gates in a candidate germ set. Also included are some additional unit tests for both this new gate penalty as well as tests for the existing op_penalty option (which penalized the total number of gate operations overall).

Corey Ostrove added 4 commits September 29, 2025 19:09
Add a new penalty option for germ selection to penalize the number of times specified gates are used. Currently only implemented for CompactEVD mode for greedy search.
Add a new option for penalizing the number of applications of a specified gate in germ selection.
Finish adding in the plumbing for the gate penalties, and add in new unit tests for the gate penalty and op penalties.
Fix the new unit tests added for testing germ selection penalties.
@coreyostrove coreyostrove added this to the 0.9.15 milestone Oct 9, 2025
@coreyostrove coreyostrove self-assigned this Oct 9, 2025
@coreyostrove coreyostrove marked this pull request as ready for review October 9, 2025 23:50
@coreyostrove coreyostrove requested review from a team and rileyjmurray as code owners October 9, 2025 23:50
Copy link
Contributor

@rileyjmurray rileyjmurray left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left several comments. Happy to iterate on them a bit!

randomization_strength=1e-2, score_func='all',
op_penalty=0.0, l1_penalty=0.0, num_nongauge_params=None,
float_type=_np.cdouble):
float_type=_np.cdouble, gate_penalty=None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type annotation please :)

Comment on lines +4351 to +4360
gate_score = 0.0
if gate_penalty is not None:
assert germ_list is not None, 'Must specify `germ_list` when using `gate_penalty`.'
for gate, penalty_value in gate_penalty.items():
#loop through each ckt in the fiducial list.
for germ in germ_list:
#alternative approach using the string
#representation of the ckt.
num_gate_instances= germ.str.count(gate)
gate_score += num_gate_instances*penalty_value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This text appears three times in this file. Please break it out into a helper function.

Comment on lines +377 to +418
class GermSelectionPenaltyTester(GermSelectionData, BaseCase):
def setUp(self):
super(GermSelectionData, self).setUp()

def test_op_penalty_greedy(self):

germs_no_penalty_cevd = germsel.find_germs(self.target_model, randomize=True, seed=1234, candidate_germ_counts={7:'all upto'},
assume_real=True, float_type=np.double, mode='compactEVD', algorithm='greedy')

germs_penalty_cevd = germsel.find_germs(self.target_model, randomize=True, seed=1234, candidate_germ_counts={7:'all upto'},
assume_real=True, float_type=np.double, mode='compactEVD', algorithm='greedy',
algorithm_kwargs={'op_penalty':.1})

assert count_ops(germs_no_penalty_cevd) > count_ops(germs_penalty_cevd)


def test_gate_penalty_greedy(self):
germs_no_penalty_alljac = germsel.find_germs(self.target_model, randomize=True, seed=1234, candidate_germ_counts={7:'all upto'},
assume_real=True, float_type=np.double, mode='all-Jac', algorithm='greedy')
germs_no_penalty_cevd = germsel.find_germs(self.target_model, randomize=True, seed=1234, candidate_germ_counts={7:'all upto'},
assume_real=True, float_type=np.double, mode='compactEVD', algorithm='greedy')

germs_penalty_alljac = germsel.find_germs(self.target_model, randomize=True, seed=1234, candidate_germ_counts={7:'all upto'},
assume_real=True, float_type=np.double, mode='all-Jac', algorithm='greedy',
algorithm_kwargs={'gate_penalty':{'Gxpi2':.1}})
germs_penalty_cevd = germsel.find_germs(self.target_model, randomize=True, seed=1234, candidate_germ_counts={7:'all upto'},
assume_real=True, float_type=np.double, mode='compactEVD', algorithm='greedy',
algorithm_kwargs={'gate_penalty':{'Gxpi2':.1}})

assert count_gate(germs_no_penalty_alljac, 'Gxpi2') > count_gate(germs_penalty_alljac, 'Gxpi2')
assert count_gate(germs_no_penalty_cevd, 'Gxpi2') > count_gate(germs_penalty_cevd, 'Gxpi2')

def test_gate_penalty_grasp(self):
germs_gate_penalty_grasp = germsel.find_germs(self.target_model, randomize=True, seed=1234, candidate_germ_counts={7:'all upto'},
assume_real=True, float_type=np.double, mode='all-Jac', algorithm='grasp',
algorithm_kwargs={'gate_penalty':{'Gxpi2':.2}, 'seed':1234, 'iterations':1})

germs_default_grasp = germsel.find_germs(self.target_model, randomize=True, seed=1234, candidate_germ_counts={7:'all upto'},
assume_real=True, float_type=np.double, mode='all-Jac', algorithm='grasp',
algorithm_kwargs={'seed':1234, 'iterations':1})

assert count_gate(germs_gate_penalty_grasp, 'Gxpi2') < count_gate(germs_default_grasp, 'Gxpi2')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The keyword arguments here are hard to read. I suggest something like the following.

class GermSelectionPenaltyTester(GermSelectionData, BaseCase):

    common_kwargs  : dict[str, Any] = dict(
        randomize=True, seed=1234, candidate_germ_counts={7:'all upto'},
        assume_real=True, float_type=np.double, mode='compactEVD', algorithm='greedy'
    )

    def setUp(self):
        super(GermSelectionData, self).setUp()

    def test_op_penalty_greedy(self):
        germs_no_penalty_cevd = germsel.find_germs(self.target_model, **self.common_kwargs)
        germs_penalty_cevd    = germsel.find_germs(self.target_model, **self.common_kwargs, algorithm_kwargs={'op_penalty':.1})        
        assert count_ops(germs_no_penalty_cevd) > count_ops(germs_penalty_cevd)


    def test_gate_penalty_greedy(self):
        kwargs = self.common_kwargs.copy()
        kwargs.pop('mode')
        germs_no_penalty_alljac = germsel.find_germs(self.target_model, **kwargs, mode='all-Jac')     
        germs_no_penalty_cevd   = germsel.find_germs(self.target_model, **kwargs, mode='compactEVD')
        germs_penalty_alljac = germsel.find_germs(
            self.target_model, **kwargs, mode='all-Jac',    algorithm_kwargs={'gate_penalty':{'Gxpi2':.1}}
        )        
        germs_penalty_cevd = germsel.find_germs(
            self.target_model, **kwargs, mode='compactEVD', algorithm_kwargs={'gate_penalty':{'Gxpi2':.1}}
        )
        assert count_gate(germs_no_penalty_alljac, 'Gxpi2') > count_gate(germs_penalty_alljac, 'Gxpi2')
        assert count_gate(germs_no_penalty_cevd,   'Gxpi2') > count_gate(germs_penalty_cevd,   'Gxpi2')
                                           
    def test_gate_penalty_grasp(self):
        kwargs = self.common_kwargs.copy()
        kwargs['mode']      = 'all-Jac'
        kwargs['algorithm'] = 'grasp'
        germs_gate_penalty_grasp = germsel.find_germs(
            self.target_model, **kwargs, algorithm_kwargs={'seed':1234, 'iterations':1, 'gate_penalty':{'Gxpi2':.2}}
        )
        germs_default_grasp = germsel.find_germs(
            self.target_model, **kwargs, algorithm_kwargs={'seed':1234, 'iterations':1}
        )
        assert count_gate(germs_gate_penalty_grasp, 'Gxpi2') < count_gate(germs_default_grasp, 'Gxpi2')

num_ops = 0
for circuit in circuits:
num_ops+= circuit.num_gates
return num_ops
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing blank line at EOF

@rileyjmurray
Copy link
Contributor

@coreyostrove, is this is an appropriate PR to look into the complex-dtype issues with compactEVD germ selection #659? It's fine with me if you'd like to handle that separately, but I figured I'd point out the chance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants