Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
NIFTy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
13
Issues
13
List
Boards
Labels
Service Desk
Milestones
Merge Requests
8
Merge Requests
8
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ift
NIFTy
Commits
12e3d597
Commit
12e3d597
authored
Sep 24, 2017
by
Martin Reinecke
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
simplify DiagonalOperator
parent
643d308b
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
93 additions
and
121 deletions
+93
-121
nifty/data_objects/numpy_do.py
nifty/data_objects/numpy_do.py
+65
-0
nifty/field.py
nifty/field.py
+3
-3
nifty/nifty_utilities.py
nifty/nifty_utilities.py
+0
-66
nifty/operators/diagonal_operator/diagonal_operator.py
nifty/operators/diagonal_operator/diagonal_operator.py
+7
-34
nifty/operators/response_operator/response_operator.py
nifty/operators/response_operator/response_operator.py
+2
-2
test/test_minimization/test_minimizers.py
test/test_minimization/test_minimizers.py
+1
-1
test/test_operators/test_composed_operator.py
test/test_operators/test_composed_operator.py
+6
-6
test/test_operators/test_diagonal_operator.py
test/test_operators/test_diagonal_operator.py
+9
-9
No files found.
nifty/data_objects/numpy_do.py
View file @
12e3d597
...
...
@@ -3,6 +3,71 @@
import
numpy
as
np
from
numpy
import
ndarray
as
data_object
from
numpy
import
full
,
empty
,
sqrt
,
ones
,
zeros
,
vdot
,
abs
,
bincount
from
..nifty_utilities
import
cast_iseq_to_tuple
,
get_slice_list
from
functools
import
reduce
def
from_object
(
object
,
dtype
=
None
,
copy
=
True
):
return
np
.
array
(
object
,
dtype
=
dtype
,
copy
=
copy
)
def
bincount_axis
(
obj
,
minlength
=
None
,
weights
=
None
,
axis
=
None
):
if
minlength
is
not
None
:
length
=
max
(
np
.
amax
(
obj
)
+
1
,
minlength
)
else
:
length
=
np
.
amax
(
obj
)
+
1
if
obj
.
shape
==
():
raise
ValueError
(
"object of too small depth for desired array"
)
data
=
obj
# if present, parse the axis keyword and transpose/reorder self.data
# such that all affected axes follow each other. Only if they are in a
# sequence flattening will be possible
if
axis
is
not
None
:
# do the reordering
ndim
=
len
(
obj
.
shape
)
axis
=
sorted
(
cast_iseq_to_tuple
(
axis
))
reordering
=
[
x
for
x
in
range
(
ndim
)
if
x
not
in
axis
]
reordering
+=
axis
data
=
np
.
transpose
(
data
,
reordering
)
if
weights
is
not
None
:
weights
=
np
.
transpose
(
weights
,
reordering
)
reord_axis
=
list
(
range
(
ndim
-
len
(
axis
),
ndim
))
# semi-flatten the dimensions in `axis`, i.e. after reordering
# the last ones.
semi_flat_dim
=
reduce
(
lambda
x
,
y
:
x
*
y
,
data
.
shape
[
ndim
-
len
(
reord_axis
):])
flat_shape
=
data
.
shape
[:
ndim
-
len
(
reord_axis
)]
+
(
semi_flat_dim
,
)
else
:
flat_shape
=
(
reduce
(
lambda
x
,
y
:
x
*
y
,
data
.
shape
),
)
data
=
np
.
ascontiguousarray
(
data
.
reshape
(
flat_shape
))
if
weights
is
not
None
:
weights
=
np
.
ascontiguousarray
(
weights
.
reshape
(
flat_shape
))
# compute the local bincount results
# -> prepare the local result array
result_dtype
=
np
.
int
if
weights
is
None
else
np
.
float
local_counts
=
np
.
empty
(
flat_shape
[:
-
1
]
+
(
length
,
),
dtype
=
result_dtype
)
# iterate over all entries in the surviving axes and compute the local
# bincounts
for
slice_list
in
get_slice_list
(
flat_shape
,
axes
=
(
len
(
flat_shape
)
-
1
,)):
current_weights
=
None
if
weights
is
None
else
weights
[
slice_list
]
local_counts
[
slice_list
]
=
np
.
bincount
(
data
[
slice_list
],
weights
=
current_weights
,
minlength
=
length
)
# restore the original ordering
# place the bincount stuff at the location of the first `axis` entry
if
axis
is
not
None
:
# axis has been sorted above
insert_position
=
axis
[
0
]
new_ndim
=
len
(
local_counts
.
shape
)
return_order
=
(
list
(
range
(
0
,
insert_position
))
+
[
new_ndim
-
1
,
]
+
list
(
range
(
insert_position
,
new_ndim
-
1
)))
local_counts
=
np
.
ascontiguousarray
(
local_counts
.
transpose
(
return_order
))
return
local_counts
nifty/field.py
View file @
12e3d597
...
...
@@ -231,8 +231,8 @@ class Field(object):
new_pindex_shape
[
ax
]
=
pindex
.
shape
[
i
]
pindex
=
np
.
broadcast_to
(
pindex
.
reshape
(
new_pindex_shape
),
field
.
shape
)
power_spectrum
=
utilities
.
bincount_axis
(
pindex
,
weights
=
field
.
val
,
axis
=
axes
)
power_spectrum
=
dobj
.
bincount_axis
(
pindex
,
weights
=
field
.
val
,
axis
=
axes
)
new_rho_shape
=
[
1
]
*
len
(
power_spectrum
.
shape
)
new_rho_shape
[
axes
[
0
]]
=
len
(
power_domain
.
rho
)
power_spectrum
/=
power_domain
.
rho
.
reshape
(
new_rho_shape
)
...
...
@@ -510,7 +510,7 @@ class Field(object):
# create a diagonal operator which is capable of taking care of the
# axes-matching
from
.operators.diagonal_operator
import
DiagonalOperator
diag
=
DiagonalOperator
(
y
.
domain
,
y
.
conjugate
(),
copy
=
False
)
diag
=
DiagonalOperator
(
y
.
conjugate
(),
copy
=
False
)
dotted
=
diag
(
x
,
spaces
=
spaces
)
return
fct
*
dotted
.
sum
(
spaces
=
spaces
)
...
...
nifty/nifty_utilities.py
View file @
12e3d597
...
...
@@ -19,8 +19,6 @@
from
builtins
import
next
,
range
import
numpy
as
np
from
itertools
import
product
from
functools
import
reduce
from
.domain_object
import
DomainObject
def
get_slice_list
(
shape
,
axes
):
...
...
@@ -74,67 +72,3 @@ def cast_iseq_to_tuple(seq):
if
np
.
isscalar
(
seq
):
return
(
int
(
seq
),)
return
tuple
(
int
(
item
)
for
item
in
seq
)
def
bincount_axis
(
obj
,
minlength
=
None
,
weights
=
None
,
axis
=
None
):
if
minlength
is
not
None
:
length
=
max
(
np
.
amax
(
obj
)
+
1
,
minlength
)
else
:
length
=
np
.
amax
(
obj
)
+
1
if
obj
.
shape
==
():
raise
ValueError
(
"object of too small depth for desired array"
)
data
=
obj
# if present, parse the axis keyword and transpose/reorder self.data
# such that all affected axes follow each other. Only if they are in a
# sequence flattening will be possible
if
axis
is
not
None
:
# do the reordering
ndim
=
len
(
obj
.
shape
)
axis
=
sorted
(
cast_iseq_to_tuple
(
axis
))
reordering
=
[
x
for
x
in
range
(
ndim
)
if
x
not
in
axis
]
reordering
+=
axis
data
=
np
.
transpose
(
data
,
reordering
)
if
weights
is
not
None
:
weights
=
np
.
transpose
(
weights
,
reordering
)
reord_axis
=
list
(
range
(
ndim
-
len
(
axis
),
ndim
))
# semi-flatten the dimensions in `axis`, i.e. after reordering
# the last ones.
semi_flat_dim
=
reduce
(
lambda
x
,
y
:
x
*
y
,
data
.
shape
[
ndim
-
len
(
reord_axis
):])
flat_shape
=
data
.
shape
[:
ndim
-
len
(
reord_axis
)]
+
(
semi_flat_dim
,
)
else
:
flat_shape
=
(
reduce
(
lambda
x
,
y
:
x
*
y
,
data
.
shape
),
)
data
=
np
.
ascontiguousarray
(
data
.
reshape
(
flat_shape
))
if
weights
is
not
None
:
weights
=
np
.
ascontiguousarray
(
weights
.
reshape
(
flat_shape
))
# compute the local bincount results
# -> prepare the local result array
result_dtype
=
np
.
int
if
weights
is
None
else
np
.
float
local_counts
=
np
.
empty
(
flat_shape
[:
-
1
]
+
(
length
,
),
dtype
=
result_dtype
)
# iterate over all entries in the surviving axes and compute the local
# bincounts
for
slice_list
in
get_slice_list
(
flat_shape
,
axes
=
(
len
(
flat_shape
)
-
1
,)):
current_weights
=
None
if
weights
is
None
else
weights
[
slice_list
]
local_counts
[
slice_list
]
=
np
.
bincount
(
data
[
slice_list
],
weights
=
current_weights
,
minlength
=
length
)
# restore the original ordering
# place the bincount stuff at the location of the first `axis` entry
if
axis
is
not
None
:
# axis has been sorted above
insert_position
=
axis
[
0
]
new_ndim
=
len
(
local_counts
.
shape
)
return_order
=
(
list
(
range
(
0
,
insert_position
))
+
[
new_ndim
-
1
,
]
+
list
(
range
(
insert_position
,
new_ndim
-
1
)))
local_counts
=
np
.
ascontiguousarray
(
local_counts
.
transpose
(
return_order
))
return
local_counts
nifty/operators/diagonal_operator/diagonal_operator.py
View file @
12e3d597
...
...
@@ -35,9 +35,7 @@ class DiagonalOperator(EndomorphicOperator):
Parameters
----------
domain : tuple of DomainObjects, i.e. Spaces and FieldTypes
The domain on which the Operator's input Field lives.
diagonal : {scalar, list, array, Field}
diagonal : Field
The diagonal entries of the operator.
copy : boolean
Internal copy of the diagonal (default: True)
...
...
@@ -68,15 +66,14 @@ class DiagonalOperator(EndomorphicOperator):
# ---Overwritten properties and methods---
def
__init__
(
self
,
domain
=
(),
diagonal
=
None
,
copy
=
True
,
default_spaces
=
None
):
def
__init__
(
self
,
diagonal
,
copy
=
True
,
default_spaces
=
None
):
super
(
DiagonalOperator
,
self
).
__init__
(
default_spaces
)
self
.
_domain
=
DomainTuple
.
make
(
domain
)
if
not
isinstance
(
diagonal
,
Field
):
raise
TypeError
(
"Field object required"
)
self
.
_diagonal
=
diagonal
if
not
copy
else
diagonal
.
copy
()
self
.
_self_adjoint
=
None
self
.
_unitary
=
None
self
.
set_diagonal
(
diagonal
=
diagonal
,
copy
=
copy
)
def
_times
(
self
,
x
,
spaces
):
return
self
.
_times_helper
(
x
,
spaces
,
operation
=
lambda
z
:
z
.
__mul__
)
...
...
@@ -119,13 +116,13 @@ class DiagonalOperator(EndomorphicOperator):
The inverse of the diagonal of the Operator.
"""
return
1.
/
self
.
diagonal
(
copy
=
False
)
return
1.
/
self
.
_diagonal
# ---Mandatory properties and methods---
@
property
def
domain
(
self
):
return
self
.
_domain
return
self
.
_d
iagonal
.
d
omain
@
property
def
self_adjoint
(
self
):
...
...
@@ -144,30 +141,6 @@ class DiagonalOperator(EndomorphicOperator):
# ---Added properties and methods---
def
set_diagonal
(
self
,
diagonal
,
copy
=
True
):
""" Sets the diagonal of the Operator.
Parameters
----------
diagonal : {scalar, list, array, Field}
The diagonal entries of the operator.
copy : boolean
Specifies if a copy of the input shall be made (default: True).
"""
# use the casting functionality from Field to process `diagonal`
f
=
Field
(
domain
=
self
.
domain
,
val
=
diagonal
,
copy
=
copy
)
# Reset the self_adjoint property:
self
.
_self_adjoint
=
None
# Reset the unitarity property
self
.
_unitary
=
None
# store the diagonal-field
self
.
_diagonal
=
f
def
_times_helper
(
self
,
x
,
spaces
,
operation
):
# if the domain matches directly
# -> multiply the fields directly
...
...
nifty/operators/response_operator/response_operator.py
View file @
12e3d597
...
...
@@ -59,8 +59,8 @@ class ResponseOperator(LinearOperator):
kernel_smoothing
=
[
FFTSmoothingOperator
(
self
.
_domain
[
x
],
sigma
[
x
])
for
x
in
range
(
nsigma
)]
kernel_exposure
=
[
DiagonalOperator
(
self
.
_domain
[
x
],
diagonal
=
exposure
[
x
])
for
x
in
range
(
nsigma
)]
kernel_exposure
=
[
DiagonalOperator
(
Field
(
self
.
_domain
[
x
],
exposure
[
x
]))
for
x
in
range
(
nsigma
)]
self
.
_composed_kernel
=
ComposedOperator
(
kernel_smoothing
)
self
.
_composed_exposure
=
ComposedOperator
(
kernel_exposure
)
...
...
test/test_minimization/test_minimizers.py
View file @
12e3d597
...
...
@@ -21,7 +21,7 @@ class Test_Minimizers(unittest.TestCase):
starting_point
=
ift
.
Field
.
from_random
(
'normal'
,
domain
=
space
)
*
10
covariance_diagonal
=
ift
.
Field
.
from_random
(
'uniform'
,
domain
=
space
)
+
0.5
covariance
=
ift
.
DiagonalOperator
(
space
,
diagonal
=
covariance_diagonal
)
covariance
=
ift
.
DiagonalOperator
(
covariance_diagonal
)
required_result
=
ift
.
Field
(
space
,
val
=
1.
)
IC
=
ift
.
DefaultIterationController
(
tol_abs_gradnorm
=
1e-5
)
...
...
test/test_operators/test_composed_operator.py
View file @
12e3d597
...
...
@@ -20,8 +20,8 @@ class ComposedOperator_Tests(unittest.TestCase):
def
test_property
(
self
,
space1
,
space2
):
rand1
=
Field
.
from_random
(
'normal'
,
domain
=
space1
)
rand2
=
Field
.
from_random
(
'normal'
,
domain
=
space2
)
op1
=
DiagonalOperator
(
space1
,
diagonal
=
rand1
)
op2
=
DiagonalOperator
(
space2
,
diagonal
=
rand2
)
op1
=
DiagonalOperator
(
rand1
)
op2
=
DiagonalOperator
(
rand2
)
op
=
ComposedOperator
((
op1
,
op2
))
if
op
.
domain
!=
(
op1
.
domain
[
0
],
op2
.
domain
[
0
]):
raise
TypeError
...
...
@@ -32,8 +32,8 @@ class ComposedOperator_Tests(unittest.TestCase):
def
test_times_adjoint_times
(
self
,
space1
,
space2
):
diag1
=
Field
.
from_random
(
'normal'
,
domain
=
space1
)
diag2
=
Field
.
from_random
(
'normal'
,
domain
=
space2
)
op1
=
DiagonalOperator
(
space1
,
diagonal
=
diag1
)
op2
=
DiagonalOperator
(
space2
,
diagonal
=
diag2
)
op1
=
DiagonalOperator
(
diag1
)
op2
=
DiagonalOperator
(
diag2
)
op
=
ComposedOperator
((
op1
,
op2
))
...
...
@@ -48,8 +48,8 @@ class ComposedOperator_Tests(unittest.TestCase):
def
test_times_inverse_times
(
self
,
space1
,
space2
):
diag1
=
Field
.
from_random
(
'normal'
,
domain
=
space1
)
diag2
=
Field
.
from_random
(
'normal'
,
domain
=
space2
)
op1
=
DiagonalOperator
(
space1
,
diagonal
=
diag1
)
op2
=
DiagonalOperator
(
space2
,
diagonal
=
diag2
)
op1
=
DiagonalOperator
(
diag1
)
op2
=
DiagonalOperator
(
diag2
)
op
=
ComposedOperator
((
op1
,
op2
))
...
...
test/test_operators/test_diagonal_operator.py
View file @
12e3d597
...
...
@@ -20,7 +20,7 @@ class DiagonalOperator_Tests(unittest.TestCase):
@
expand
(
product
(
spaces
,
[
True
,
False
]))
def
test_property
(
self
,
space
,
copy
):
diag
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
D
=
DiagonalOperator
(
space
,
diagonal
=
diag
)
D
=
DiagonalOperator
(
diag
)
if
D
.
domain
[
0
]
!=
space
:
raise
TypeError
if
D
.
unitary
!=
False
:
...
...
@@ -33,7 +33,7 @@ class DiagonalOperator_Tests(unittest.TestCase):
rand1
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
rand2
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
diag
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
D
=
DiagonalOperator
(
space
,
diagonal
=
diag
,
copy
=
copy
)
D
=
DiagonalOperator
(
diag
,
copy
=
copy
)
tt1
=
rand1
.
vdot
(
D
.
times
(
rand2
))
tt2
=
rand2
.
vdot
(
D
.
times
(
rand1
))
assert_approx_equal
(
tt1
,
tt2
)
...
...
@@ -42,7 +42,7 @@ class DiagonalOperator_Tests(unittest.TestCase):
def
test_times_inverse
(
self
,
space
,
copy
):
rand1
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
diag
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
D
=
DiagonalOperator
(
space
,
diagonal
=
diag
,
copy
=
copy
)
D
=
DiagonalOperator
(
diag
,
copy
=
copy
)
tt1
=
D
.
times
(
D
.
inverse_times
(
rand1
))
assert_allclose
(
rand1
.
val
,
tt1
.
val
)
...
...
@@ -50,7 +50,7 @@ class DiagonalOperator_Tests(unittest.TestCase):
def
test_times
(
self
,
space
,
copy
):
rand1
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
diag
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
D
=
DiagonalOperator
(
space
,
diagonal
=
diag
,
copy
=
copy
)
D
=
DiagonalOperator
(
diag
,
copy
=
copy
)
tt
=
D
.
times
(
rand1
)
assert_equal
(
tt
.
domain
[
0
],
space
)
...
...
@@ -58,7 +58,7 @@ class DiagonalOperator_Tests(unittest.TestCase):
def
test_adjoint_times
(
self
,
space
,
copy
):
rand1
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
diag
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
D
=
DiagonalOperator
(
space
,
diagonal
=
diag
,
copy
=
copy
)
D
=
DiagonalOperator
(
diag
,
copy
=
copy
)
tt
=
D
.
adjoint_times
(
rand1
)
assert_equal
(
tt
.
domain
[
0
],
space
)
...
...
@@ -66,7 +66,7 @@ class DiagonalOperator_Tests(unittest.TestCase):
def
test_inverse_times
(
self
,
space
,
copy
):
rand1
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
diag
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
D
=
DiagonalOperator
(
space
,
diagonal
=
diag
,
copy
=
copy
)
D
=
DiagonalOperator
(
diag
,
copy
=
copy
)
tt
=
D
.
inverse_times
(
rand1
)
assert_equal
(
tt
.
domain
[
0
],
space
)
...
...
@@ -74,20 +74,20 @@ class DiagonalOperator_Tests(unittest.TestCase):
def
test_adjoint_inverse_times
(
self
,
space
,
copy
):
rand1
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
diag
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
D
=
DiagonalOperator
(
space
,
diagonal
=
diag
,
copy
=
copy
)
D
=
DiagonalOperator
(
diag
,
copy
=
copy
)
tt
=
D
.
adjoint_inverse_times
(
rand1
)
assert_equal
(
tt
.
domain
[
0
],
space
)
@
expand
(
product
(
spaces
,
[
True
,
False
]))
def
test_diagonal
(
self
,
space
,
copy
):
diag
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
D
=
DiagonalOperator
(
space
,
diagonal
=
diag
,
copy
=
copy
)
D
=
DiagonalOperator
(
diag
,
copy
=
copy
)
diag_op
=
D
.
diagonal
()
assert_allclose
(
diag
.
val
,
diag_op
.
val
)
@
expand
(
product
(
spaces
,
[
True
,
False
]))
def
test_inverse
(
self
,
space
,
copy
):
diag
=
Field
.
from_random
(
'normal'
,
domain
=
space
)
D
=
DiagonalOperator
(
space
,
diagonal
=
diag
,
copy
=
copy
)
D
=
DiagonalOperator
(
diag
,
copy
=
copy
)
diag_op
=
D
.
inverse_diagonal
()
assert_allclose
(
1.
/
diag
.
val
,
diag_op
.
val
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment