Skip to content

Commit

Permalink
Modernise Cython property syntax.
Browse files Browse the repository at this point in the history
This change instances of the deprecated property: syntax in
Cython to standard Python @Property.  In addition, all properties
now have an associated docstring.

Closes #337.

Update properties in action/behav/game/infoset/mixed.

Update property syntax in remaining classes.
  • Loading branch information
tturocy committed Aug 14, 2023
1 parent 8f7a0a4 commit 665ec81
Show file tree
Hide file tree
Showing 11 changed files with 515 additions and 457 deletions.
81 changes: 43 additions & 38 deletions src/pygambit/core/action.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
import typing

from libcpp.string cimport string

cdef class Action:
Expand Down Expand Up @@ -61,45 +63,48 @@ cdef class Action:
def precedes(self, node: Node) -> bool:
return self.action.deref().Precedes((<Node>node).node)

property label:
def __get__(self):
return self.action.deref().GetLabel().decode('ascii')
def __set__(self, value):
cdef string s
s = value.encode('ascii')
self.action.deref().SetLabel(s)
@property
def label(self) -> str:
"""Get or set the text label of the action."""
return self.action.deref().GetLabel().decode('ascii')

property infoset:
def __get__(self):
cdef Infoset i
i = Infoset()
i.infoset = self.action.deref().GetInfoset()
return i
@label.setter
def label(self, value: str) -> None:
self.action.deref().SetLabel(value.encode('ascii'))

property prob:
def __get__(self):
cdef string py_string
py_string = <string>(
self.action.deref().GetInfoset().deref().GetActionProb(
self.action.deref().GetNumber()
)
)
if "." in py_string.decode('ascii'):
return decimal.Decimal(py_string.decode('ascii'))
else:
return Rational(py_string.decode('ascii'))

def __set__(self, value: typing.Any):
"""
Set the probability a chance action is played.
@property
def infoset(self) -> Infoset:
"""Get the information set to which the action belongs."""
cdef Infoset i
i = Infoset()
i.infoset = self.action.deref().GetInfoset()
return i

@property
def prob(self) -> typing.Union[decimal.Decimal, Rational]:
"""
Get or set the probability a chance action is played.

Parameters
----------
value : Any
The probability the action is played. This can be any numeric type, or any object that
has a string representation which can be interpreted as an integer,
decimal, or rational number.
"""
self.action.deref().GetInfoset().deref().SetActionProb(
self.action.deref().GetNumber(), _to_number(value)
Parameters
----------
value : Any
The probability the action is played. This can be any numeric type, or any object that
has a string representation which can be interpreted as an integer,
decimal, or rational number.
"""
cdef string py_string
py_string = <string>(
self.action.deref().GetInfoset().deref().GetActionProb(
self.action.deref().GetNumber()
)
)
if "." in py_string.decode('ascii'):
return decimal.Decimal(py_string.decode('ascii'))
else:
return Rational(py_string.decode('ascii'))

@prob.setter
def prob(self, value: typing.Any) -> None:
self.action.deref().GetInfoset().deref().SetActionProb(
self.action.deref().GetNumber(), _to_number(value)
)
30 changes: 17 additions & 13 deletions src/pygambit/core/behav.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import functools

from cython.operator cimport dereference as deref

cdef class MixedBehaviorProfile(object):
cdef class MixedBehaviorProfile:
def __repr__(self):
return str([ self[player] for player in self.game.players ])
def _repr_latex_(self):
Expand Down Expand Up @@ -295,12 +295,14 @@ cdef class MixedBehaviorProfileDouble(MixedBehaviorProfile):
else:
self.profile.Randomize(denom)

property game:
def __get__(self):
cdef Game g
g = Game()
g.game = self.profile.GetGame()
return g
@property
def game(self) -> Game:
"""The game on which this mixed behaviour profile is defined.
"""
cdef Game g
g = Game()
g.game = self.profile.GetGame()
return g


cdef class MixedBehaviorProfileRational(MixedBehaviorProfile):
Expand Down Expand Up @@ -368,9 +370,11 @@ cdef class MixedBehaviorProfileRational(MixedBehaviorProfile):
def randomize(self, denom):
self.profile.Randomize(denom)

property game:
def __get__(self):
cdef Game g
g = Game()
g.game = self.profile.GetGame()
return g
@property
def game(self) -> Game:
"""The game on which this mixed behaviour profile is defined.
"""
cdef Game g
g = Game()
g.game = self.profile.GetGame()
return g
205 changes: 104 additions & 101 deletions src/pygambit/core/game.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -376,134 +376,137 @@ cdef class Game:
def __hash__(self):
return long(<long>self.game.deref())

property is_tree:
@property
def is_tree(self) -> bool:
"""Returns whether a game has a tree-based representation."""
def __get__(self) -> bool:
return self.game.deref().IsTree()
return self.game.deref().IsTree()

property title:
@property
def title(self) -> str:
"""Gets or sets the title of the game. The title of the game is
an arbitrary string, generally intended to be short."""
def __get__(self) -> str:
"""Gets the title of the game."""
return self.game.deref().GetTitle().decode('ascii')
return self.game.deref().GetTitle().decode('ascii')

def __set__(self, value: str) -> None:
"""Sets the title of the game."""
self.game.deref().SetTitle(value.encode('ascii'))
@title.setter
def title(self, value: str) -> None:
self.game.deref().SetTitle(value.encode('ascii'))

property comment:
@property
def comment(self) -> str:
"""Gets or sets the comment of the game. A game's comment is
an arbitrary string, and may be more discursive than a title."""
def __get__(self) -> str:
"""Gets the comment of the game."""
return self.game.deref().GetComment().decode('ascii')
def __set__(self, value: str) -> None:
"""Sets the comment of the game."""
self.game.deref().SetComment(value.encode('ascii'))

property actions:
def __get__(self):
cdef GameActions a
if self.is_tree:
a = GameActions()
a.game = self.game
return a
raise UndefinedOperationError(
"Operation only defined for games with a tree representation"
)
return self.game.deref().GetComment().decode('ascii')

property infosets:
def __get__(self):
cdef GameInfosets i
if self.is_tree:
i = GameInfosets()
i.game = self.game
return i
raise UndefinedOperationError(
"Operation only defined for games with a tree representation"
)
@comment.setter
def comment(self, value: str) -> None:
self.game.deref().SetComment(value.encode('ascii'))

property players:
def __get__(self):
cdef Players p
p = Players()
p.game = self.game
return p
@property
def actions(self) -> GameActions:
"""Return the set of actions available in the game.

property strategies:
def __get__(self):
cdef GameStrategies s
s = GameStrategies()
s.game = self.game
return s
Raises
------
UndefinedOperationError
If the game does not have a tree representation.
"""
cdef GameActions a
if self.is_tree:
a = GameActions()
a.game = self.game
return a
raise UndefinedOperationError(
"Operation only defined for games with a tree representation"
)

property outcomes:
def __get__(self):
cdef Outcomes c
c = Outcomes()
c.game = self.game
return c
@property
def infosets(self) -> GameInfosets:
"""Return the set of information sets in the game.

property contingencies:
def __get__(self):
return pygambit.gameiter.Contingencies(self)
Raises
------
UndefinedOperationError
If the game does not have a tree representation.
"""
cdef GameInfosets i
if self.is_tree:
i = GameInfosets()
i.game = self.game
return i
raise UndefinedOperationError(
"Operation only defined for games with a tree representation"
)

@property
def players(self) -> Players:
"""Return the set of players in the game."""
cdef Players p
p = Players()
p.game = self.game
return p

property root:
@property
def strategies(self) -> GameStrategies:
"""Return the set of strategies in the game."""
cdef GameStrategies s
s = GameStrategies()
s.game = self.game
return s

@property
def outcomes(self) -> Outcomes:
"""Return the set of outcomes in the game."""
cdef Outcomes c
c = Outcomes()
c.game = self.game
return c

@property
def contingencies(self) -> pygambit.gameiter.Contingencies:
"""Return an iterator over the contingencies in the game."""
return pygambit.gameiter.Contingencies(self)

@property
def root(self) -> Node:
"""Returns the root node of the game.
Returns
-------
Node
The root node of the game.

Raises
------
UndefinedOperationError
If the game does not hae a tree representation.
"""
def __get__(self) -> Node:
cdef Node n
if self.is_tree:
n = Node()
n.node = self.game.deref().GetRoot()
return n
raise UndefinedOperationError(
"Operation only defined for games with a tree representation"
)

property is_const_sum:
"""Returns whether the game is constant sum.

Returns
-------
bool
`True` if and only if the game is constant sum.
"""
def __get__(self) -> bool:
return self.game.deref().IsConstSum()

property is_perfect_recall:
cdef Node n
if self.is_tree:
n = Node()
n.node = self.game.deref().GetRoot()
return n
raise UndefinedOperationError(
"Operation only defined for games with a tree representation"
)

@property
def is_const_sum(self) -> bool:
"""Returns whether the game is constant sum."""
return self.game.deref().IsConstSum()

@property
def is_perfect_recall(self) -> bool:
"""Returns whether the game is perfect recall.

By convention, games with a strategic representation have perfect recall as they
are treated as simultaneous-move games.

Returns
-------
bool
`True` if and only if the game is perfect recall.
"""
def __get__(self) -> bool:
return self.game.deref().IsPerfectRecall()
return self.game.deref().IsPerfectRecall()

property min_payoff:
def __get__(self):
return rat_to_py(self.game.deref().GetMinPayoff(0))
@property
def min_payoff(self) -> typing.Union[decimal.Decimal, Rational]:
"""Returns the minimum payoff in the game."""
return rat_to_py(self.game.deref().GetMinPayoff(0))

property max_payoff:
def __get__(self):
return rat_to_py(self.game.deref().GetMaxPayoff(0))
@property
def max_payoff(self) -> typing.Union[decimal.Decimal, Rational]:
"""Returns the maximum payoff in the game."""
return rat_to_py(self.game.deref().GetMaxPayoff(0))

def _get_contingency(self, *args):
cdef c_PureStrategyProfile *psp
Expand Down
Loading

0 comments on commit 665ec81

Please sign in to comment.